poll.py --check reports CHANGED every tick: the session board and closing_refs escape the per-session scoping nf-frjf ← Beads

closed priority 2 task unassigned

Twinned from https://github.com/cfsh/ninefold/issues/623 by tools/beads/import_github.py's reconcile pass.

Filed unclaimed from nf-dev-history-charter (Linux gating box). Distinct from #587, which is the *unlabelled* case — this is the scoped path failing for a different reason.

Symptom

--check has returned CHANGED on every tick for four consecutive ticks, with IDLE_TICKS=0 each time, while my tracked PR sat unchanged at CONVERGED. So:

Cause: two repo-wide inputs survive the per-session scoping

compute_fingerprint was deliberately scoped — its own comment says why:

> Scoped to THIS session when $slabel is set: repo-wide made --check return CHANGED on nearly every daytime tick because of OTHER sessions' traffic.

Two inputs escape that scoping, and both scale with repo activity rather than this session's work:

1. meta counts unconditionally (poll.py:485) — if "meta" in names or slabel in names. There is exactly one meta issue, #398, the session board, and it is the single most-written issue in the repo when several sessions run: every session edits its own row, and the rule is *edit the body, never comment*, so each edit bumps updatedAt for everyone.

2. closing_refs scans every open PR body (poll.py:494) for Closes/Refs #n, and the result is appended to the fingerprint. Any session editing any of the 10 open PR bodies — including adding a Refs line, which review rounds routinely produce — perturbs it.

The intent behind including meta is sound (*"the board is where this session gets redirected"*). The side effect is that a signal meant to mean *something changed for me* now means *something changed for anyone*.

Measurement

My tracked PR's entire fingerprint contribution is provably static:

``` headRefOid ea1c84f unchanged since 09:30:53Z aedanpope reviews 0 aedanpope comments 0 inline comments 0 GAPS verdicts 4 historical, submittedAt values are immutable ```

Note the CONVERGED verdict correctly contributes nothing — #341's rule that a PASS carries no work is working as designed.

Meanwhile:

``` meta #398 updatedAt = 2026-08-06T10:50:19Z <- another session's row edit, not mine (my last board write was ~09:52) ```

Nothing about my PR changed; the board did; --check said CHANGED. Four ticks, same story.

Why it matters more with the box full

This is a multi-session amplifier: with N live sessions all editing #398, each session's quiet-tick probability falls roughly as the others' write rate rises. Four dev sessions are live on this box right now, which is precisely when the ~2min digest is most expensive — 2 cores, already contended (#605 is a test flaking from that contention).

It also silently defeats a documented safety property. CLAUDE.md separates stopping (gate on existence) from cadence (gate on change) and warns that conflating them gets both wrong. The cadence half is currently inert, and nothing says so — POLL_CADENCE keeps printing a ladder that cannot be climbed.

Directions, for whoever takes it

Not obvious which is right; the tension is real, since the board *is* a channel this session must not miss.

Related but distinct: #587 (unlabelled sessions get ~130 issues in the fingerprint — the else branch); #540 (the ladder counts invocations, not elapsed time). This is a third, independent way the same ladder fails to work.

Refs #587, #540, #600

Dependencies

None.

Comments

No comments.

Add a comment