Skip to content

Check the issue timeline read against its total - #5

Merged
timlichtenberg merged 3 commits into
mainfrom
tl/timeline-guard
Oct 23, 2026
Merged

timlichtenberg merged 3 commits into
mainfrom
tl/timeline-guard

Conversation

@timlichtenberg

Copy link
Copy Markdown
Member

Summary

The issue values read takes the change time of each synced issue field from the issue timeline, but it trusts the timeline list as it comes back. If the API returns a short or empty timeline with no error, a synced value has no change time and loses to any value set at another place, so the run can overwrite a newer issue edit. This change checks the timeline against its totalCount and keeps an issue out of the run when a synced value has no event.

No short timeline has been seen yet: the board and value reads came back short on 22 Oct, and this closes the same gap for the timeline before it shows up.

Changes

  • The timeline query asks for totalCount. A timeline with a number of events other than min(totalCount, 100), a null or missing totalCount, or a null event list is a short read (a null event counts as a missing one, as null entries in the value lists already do): the batch is read again (up to 6 reads, 10 seconds apart), then the run stops before any write, as for a short value list.
  • An issue with a value of a synced field (Start date, Target date, Priority) but no event for that field, while its timeline holds 100 field events or fewer, counts as unread for the run, so its issue fields are neither read nor written. Effort is derived and not checked.
  • An issue with more than 100 field events keeps the current rule: a value whose event is older than the last 100 has no time.
  • README: the two rules.

Validation

  • python -m pytest -q: 210 passed.
  • coverage run -m pytest -q && coverage report: 99% total, github.py 100% of lines and branches.
  • ruff check . and ruff format --check .: clean.
  • I changed each new condition in turn (13 changes, for example != to <, <= to <, any to all, the isinstance check, the 100 limit, the placeholder replace); every change makes at least 1 test fail, and 2 changes without effect make none fail.
  • Live, with a personal token: on 40 issues the timeline totalCount with itemTypes equals the number of events returned and differs from the count without the filter. In a full read of the 3 boards, 120 of 293 issues hold synced values and none has a value without an event. python -m board_sync without --apply on this branch: all reads full, 0 unread issues.
  • The 5 snapshots in the state repository, read with the App token from 22 Oct on, hold 65 to 169 issues with synced issue values each, and none has a synced value without a time, so the unread rule would have kept no issue out of those runs.

Not checked

  • Whether GitHub writes a timeline event for every way a value can be set (issue transfer, values set at creation). If one does not, that issue stays unread in every run, and the run log gives only the number of unread issues, not which ones.
  • An issue with more than 100 field events: none of the sampled issues has more than 5. Each sync write adds an event, so an issue can reach the limit in time; reading older pages of the timeline would close that.
  • One issue whose timeline stays short stops every run after 6 reads of its batch, as a short value list already does; the read count and the wait are unchanged.
  • The new checks were not run with the App token the workflow uses.

An issue timeline read can come back short or empty with no error, and a synced value then has no change time and loses to any other place. The timeline query now asks for totalCount: a read with a number of field events other than min(totalCount, 100) is read again like the other short reads. An issue with a synced value but no event for it, while the timeline holds 100 field events or fewer, counts as unread for the run, so its issue fields are not written.
A null or absent totalCount on the issue timeline raised TypeError or KeyError and stopped the run without a second read; it now counts as a short read like the value list. Tests cover a timeline total of 101, two synced values of which only one has an event, more events than the total, and the query placeholders.
The docstrings now say that any event count other than the total, and a missing total, lead to a new read, and that an unread issue waits for the next run. The constant comment no longer claims an event order.
@timlichtenberg
timlichtenberg merged commit 433f3d7 into main Oct 23, 2026
1 check passed
@timlichtenberg
timlichtenberg deleted the tl/timeline-guard branch October 23, 2026 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant