Skip to content

feat: handle full rerun by resetting the rerun key instead of crashing - #27

Merged
fcsonline merged 1 commit into
mainfrom
full-rerun-reset
Jul 29, 2026
Merged

feat: handle full rerun by resetting the rerun key instead of crashing#27
fcsonline merged 1 commit into
mainfrom
full-rerun-reset

Conversation

@fcsonline

@fcsonline fcsonline commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Port of the Ruby version's factorialco/specbandit#26 to the JS version.

Problem

The work-mode decision table treated "published + shared queue has data + rerun key has data" as a weird/inconsistent state and crashed with exit 1. That state occurs legitimately: a full rerun, where the queue is re-pushed while a runner still carries rerun memory from a previous run.

Change

Instead of crashing, the worker now:

  1. Prints a notice that a full rerun was detected.
  2. Deletes the stale rerun key (new RedisQueue#delete, DEL under the usual retry wrapper) before the first steal, so it cannot wipe freshly recorded batches.
  3. Steals from the shared queue like a classic run with recording enabled, rebuilding the rerun memory from the batches this run actually executes.

Updated decision table:

Published Shared queue Rerun key Behavior
No -- -- Crash (exit 1)
Yes Drained Empty OK, worker arriving late (exit 0)
Yes Has data Empty Steal (classic run)
Yes Drained Has data Replay (classic rerun)
Yes Has data Has data Full rerun: reset rerun key, steal from queue (was: crash)

Tests

  • Rewrote the "weird case" test in worker.test.ts as a full-rerun suite: asserts the delete happens before any steal, stolen batches are re-recorded to the rerun key, exit 0, and the output explains the reset.
  • Added a #delete spec to redisQueue.test.ts.
  • npm test — 141 tests, 0 failures. npm run typecheck clean.

README decision table and Redis-command summary updated accordingly.

No version bump included, matching the Ruby PR.

Port of factorialco/specbandit#26. When the shared queue was re-pushed
while a runner still holds rerun memory from a previous run (a full
rerun), delete the stale rerun key and steal from the shared queue like
a classic run, re-recording as we go — instead of crashing with exit 1.

@beagleknight beagleknight left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@fcsonline
fcsonline merged commit b3bcd66 into main Jul 29, 2026
3 checks passed
beagleknight added a commit that referenced this pull request Aug 12, 2026
…28)

Mirrors specbandit (Ruby) #27.

The queue key is scoped by CI run, not by CI attempt. That is deliberate:
re-running a single failed runner does not re-run the job that pushed, so
the runner must still find the queue and the published marker the first
attempt created.

The cost is that the producer is not idempotent. A producer that pushes
and then fails, or that is re-run with the whole workflow, appends a
second copy of the work list. Every file is then enqueued twice, the
suite runs twice, and any two copies that reach the same worker are
loaded twice in one process.

Add RedisQueue#clear, which removes the queue and its published marker in
one DEL, and expose it two ways:

- `specbandit push --reset` empties the key immediately before the RPUSH.
- `specbandit reset --key KEY` does the same as a standalone command.

Reset is opt-in, so `push` keeps its current meaning and appending to a
queue stays possible.

`reset` is registered as a boolean flag in the argument parser. A flag
missing from that list consumes the next token as its value, so
`push --reset a.test.ts` would otherwise lose the file.

Per-runner rerun and failed keys are left alone, so a single-runner
re-run can still replay its own files. Nothing is cleared when there is
nothing to push in its place: dropping the marker on its own would make
every worker on that key crash as "never published".

Also closes a version drift. The VERSION constant said 0.10.0 while the
package was at 1.3.0, so `--version` and the report JSON both reported a
version nobody shipped. It cannot import package.json, because tsconfig
rootDir is src, so the release workflow now rewrites it alongside the
package.json bump.
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.

2 participants