Skip to content

fix: discover predecessors when GitHub status indexes are stale - #170

Merged
chenrui333 merged 2 commits into
masterfrom
fix/165-stale-run-discovery
Nov 5, 2026
Merged

chenrui333 merged 2 commits into
masterfrom
fix/165-stale-run-discovery

Conversation

@chenrui333

@chenrui333 chenrui333 commented Nov 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes #165.

Restore predecessor discovery when GitHub's status-filtered run index is stale, for both workflow-specific discovery and repository-wide queue-name discovery. Rebuild the checked-in action bundle.

#142 combined unfiltered discovery with active-status queries to handle the stale index documented in #35. #151 fixed #146's expensive history walks and per-workflow queue fanout, but removed the unfiltered source entirely. Consequently, three empty indexed responses could make Turnstyle exit while an earlier run was active.

Notes

Failing-before / passing-after reproduction

The first commit (d19a8a2) adds only regression tests against unchanged production code from 205d0f5 (v3.3.4).

npx vitest run __tests__/github.test.ts -t 'issue #165'
Before: 4 failed
- workflow and repository discovery: expected [active predecessor], received []
- ordinary and queue-name waiters: expected a wait for that predecessor, no wait logged
After the discovery fix: all 4 original cases passed

The fixture has an earlier in_progress run, a later active run, empty responses for all three status filters, and both runs present in the unfiltered response. Fake timers prove the later run remains blocked until the predecessor completes. Coverage also includes current-run reruns, client-side branch/queue filtering, absent and completed predecessors, stale indexed candidates that have completed, recovery on the 500th recent run, older indexed candidates, and unfiltered pagination/retry cancellation at deadlines. Existing ordering and job/step waiting tests remain green.

Bounded recovery, not a return to #146

Keep the existing three active-status scans and their 50-page caps, preserving discovery of older active runs without consuming the completed-history budget. Always reconcile at most five unfiltered pages (100 runs per page), without server-side branch/status filters. This is not an empty-results-only fallback: a partially stale index must not hide another predecessor. Recent unfiltered snapshots override indexed entries, including removing completed runs.

Recovery from a stale index is bounded to the most recent 500 runs per endpoint, across all branches; older candidates still depend on the active-status queries. Pages with completed/nonmatching runs do not cause early termination. Queue-name discovery remains repository-level; there is no per-workflow fanout.

Run-list request budgets per poll, excluding current-run metadata and the initial repository-workflow lookup (unchanged); retries can add attempts but remain bound by the shared deadline:

Scenario v3.3.4 This PR
Workflow, empty indexes and deep completed history 3 8
Queue-name, empty indexes and deep completed history 6 16
Workflow, all three active scans reach their existing caps 150 155
Queue-name, both discovery endpoints reach all caps 300 310

The compiled-action smoke used a local HTTP fixture with 39 workflows and 10,000 historical runs, returned the predecessor on the fifth unfiltered page, and verified waiting through completion in both modes. These are measured counts for the empty-index/deep-history rows, independent of workflow count. HTTP-level tests also verify the fifth-page cap through the real Octokit paginator, shared abort signal on a pending second page, monotonic deadline checks before a subsequent page, and cancellation of an unfiltered 5xx retry.

Verification

  • npm ci --registry=https://registry.npmjs.org
  • npm run typecheck
  • npm run test -- --run: 195 tests passed; coverage thresholds passed
  • npm run fmtcheck
  • npm run build
  • node --test .github/scripts/dependabot-bundle.test.cjs: 21 passed
  • Compiled-action HTTP smoke: workflow and queue-name waiting/completion passed
  • GitHub Actions run 42040212755: build passed on 33e7c38b0773e8876aa5966c28301578d313aa06, including the bundle-freshness check. The integration job is push-only and is skipped for PRs; the compiled-action HTTP smoke above exercises waiting before merge.

No live intermittent-index reproduction is claimed. The deterministic regression and compiled-action smoke do not depend on GitHub indexing behavior.

Per maintainer direction, changelog/version updates will be a separate direct commit to master after the maintainer merges this PR, followed by the release. This PR does not move release tags.

References

Signed-off-by: Rui Chen <rui@chenrui.dev>
Signed-off-by: Rui Chen <rui@chenrui.dev>
@chenrui333
chenrui333 merged commit dbb2c76 into master Nov 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant