Skip to content

fix: treat an item created since the last poll as genuinely new - #53

Merged
maxigimenez merged 2 commits into
mainfrom
fix/newly-created-items
Sep 7, 2026
Merged

maxigimenez merged 2 commits into
mainfrom
fix/newly-created-items

Conversation

@maxigimenez

Copy link
Copy Markdown
Owner

Follow-up to #52, which was necessary but not sufficient. A pr_review_requested route matching reviewersAdded still could not fire for the case that actually occurs in practice.

The evidence

PR #408 created   13:19:18   by JamesAIBot
review requested  13:19:21   EomiAIBot

Three seconds apart. The runner's first ever sighting of that pull request already had the reviewer attached, so first sight suppressed the transition, seeded the baseline, and the route went quiet forever. Confirmed against the live runner:

gs-racing-tech/tracksidecc#408 | ["EomiAIBot"] | 2026-09-07 16:29:23

Every transition empty, and no way back.

This is the dominant shape in a bot-to-bot workflow, and it isn't exotic otherwise — filling in the reviewer field on the Create pull request form does the same, as does filing a ticket with its label already on.

The fix

"I have never seen this" and "this did not exist" are different facts, and conflating them is what made the whole class of route impossible. Staying quiet is right for backlog — otherwise creating a route fires it across everything currently open — and wrong for an item created since the last poll, where everything on it really was just added.

  • project_watch holds a per-project watermark: the newest item-creation timestamp seen. Anything newer came into existence while we were watching.
  • Both sides of that comparison are the provider's clock, deliberately. Comparing GitHub's timestamps against this machine's would make a runner with a slow clock replay its backlog.
  • Read once per cycle and advanced after it, so two items opened between the same pair of polls cannot silence each other by ordering. Monotonic, because a reopened item can arrive older than one already seen.
  • Set on a project's first cycle, so everything open at that moment is backlog and stays quiet. That is the guard working, not a gap.
  • TriggerEvent.createdAt carries the fact — created_at on GitHub issues and pulls, createdAt on Linear issues. It is never matched on.

One implementation

observeCycle now owns the per-cycle observation step, called by the poll loop and driven directly by the tests. Both bugs on this branch shipped past a green suite whose helpers re-implemented that loop, and the unit tests handed changes to the rule engine directly — so neither could see that the real pipeline never produced it.

Covered

Fires for a PR born since the last poll · stays quiet for one that predates the first look · does not re-fire on later cycles · lets two PRs opened between the same polls both fire · does not replay a twenty-PR backlog on a project's first cycle · does not treat an older PR as new once the watermark has moved. Plus the watermark's own monotonicity and per-project isolation, and that sentinel0 explain reports the new-item verdict without moving the watermark.

572 tests pass across all five packages; lint and build clean.

Deploying

The watermark is empty on first boot, so the current backlog stays quiet — including PR #408, which is waiting on a review right now. Re-request the review on it to arm it. PRs opened after that first cycle fire on their own.

Releases 0.0.6.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QNEqbFkMFoPiiVnJGxpjj2

maxigimenez and others added 2 commits September 7, 2026 17:34
The first fix was necessary and not sufficient. `pr_review_requested` +
`reviewersAdded` still could not fire for the case that actually occurs here:

    PR #408 created  13:19:18  by JamesAIBot
    review requested 13:19:21  EomiAIBot

Three seconds. The runner's *first ever* sighting of that pull request already
had the reviewer attached, so first sight suppressed the transition, seeded the
baseline, and the route went quiet forever. Confirmed against the live runner:
the observation row reads `["EomiAIBot"]` with every transition empty.

This is the dominant shape in a bot-to-bot workflow, and it is not exotic
otherwise: filling the reviewer field in the "Create pull request" form does
the same, as does filing a ticket with its label already on.

"I have never seen this" and "this did not exist" are different facts, and
conflating them is what made the class of route impossible. Staying quiet is
right for backlog — otherwise creating a route fires it across everything open
— and wrong for an item created since the last poll, where everything on it
really was just added.

- `project_watch` holds a per-project watermark: the newest item-creation
  timestamp seen. Anything newer came into existence while we were watching.
  Both sides of that comparison are the *provider's* clock, deliberately;
  comparing GitHub's timestamps against this machine's would make a runner with
  a slow clock replay its backlog.
- The watermark is read once per cycle and advanced after it, so two items
  opened between the same pair of polls cannot silence each other by ordering,
  and it is monotonic because a reopened item can arrive older than one already
  seen.
- It is set on a project's first cycle, so everything open at that moment is
  backlog and stays quiet. That is the guard working, not a gap.
- `TriggerEvent.createdAt` carries the fact, from `created_at` on GitHub issues
  and pulls and `createdAt` on Linear issues. It is not matched on.

`observeCycle` now owns the per-cycle observation step, called by the poll loop
and driven directly by the tests. Both bugs in this branch shipped past a green
suite whose helpers re-implemented that loop; there is one implementation now.

Covered: fires for a PR born since the last poll, stays quiet for one that
predates the first look, does not re-fire on later cycles, lets two PRs opened
between the same polls both fire, does not replay a twenty-PR backlog on a
project's first cycle, and does not treat an older PR as new once the watermark
has moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNEqbFkMFoPiiVnJGxpjj2
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QNEqbFkMFoPiiVnJGxpjj2
@maxigimenez
maxigimenez merged commit 56734f0 into main Sep 7, 2026
2 checks passed
@maxigimenez
maxigimenez deleted the fix/newly-created-items branch September 7, 2026 16:53
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