Skip to content

fix(runner): stop re-running files a previous build correctly skipped - #63

Merged
damusix merged 1 commit into
masterfrom
fix/build-rerun-after-skip
Jul 29, 2026
Merged

fix(runner): stop re-running files a previous build correctly skipped#63
damusix merged 1 commit into
masterfrom
fix/build-rerun-after-skip

Conversation

@damusix

@damusix damusix commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Found while smoke-testing a locally-built binary from master. Deterministic, 100% reproducible, and it breaks the single most common thing anyone does with this tool: running run build more than twice.

Repro

Empty sqlite DB, one non-idempotent CREATE TABLE, build.include: [01_tables]:

build 1: {"status":"success","filesRun":1,"filesSkipped":0}
build 2: {"status":"success","filesRun":0,"filesSkipped":1}   <- correct skip
build 3: {"status":"failed","filesFailed":1}                  <- re-executes, "table widget already exists"
build 4+: failed forever

Cause

Tracker.needsRun() (src/core/runner/tracker.ts) forced a re-run for any skipped execution record. But skipped carries two distinct meanings:

  • cascade skip — an earlier file in the batch failed, so this one never executed. Must re-run.
  • skip_reason: 'unchanged' — the recorded outcome of a correct skip. Must not re-run.

Upfront placeholders from createFileRecords are 'pending', not 'skipped', so the pending half of that condition was already doing the job its comment described. The skipped half was over-broad.

So build 2 skipped correctly and wrote unchanged; build 3 read that as "never ran" and re-executed. Once it failed, the record read failed, and the retry branch made every later build fail too.

Fix

Select skip_reason, and only force a run for pending or a non-unchanged skip. An unchanged skip falls through to the existing stale check and checksum comparison — so a genuinely edited file still re-runs.

Verified end to end against the built binary:

build 1: runs        build 2: skips       build 3: skips
after edit: RE-RUNS  then: skips          then: skips

Likely closes #54

#54 was filed as "run build intermittently fails on objects it just created" and explicitly asked to rule double execution in or out at the source. It isn't intermittent — it depends on how many times that file had already been built. The reporter's read ("for the error to fire on an empty database, that statement has to have run before") was correct.

I'd let the reporter confirm against their MSSQL tree before closing it outright, since their repro had a different shape (failure inside a first build over 9 phase directories).


Verified: lint, typecheck, build clean. All four CI groups against live pg/mysql/mssql — 2592 / 122 / 476 / 631 = 3821 pass, 0 fail. 6 new tracker tests; with the buggy condition restored, the regression test fails, along with the checksum and stale fall-through cases — confirming those paths were unreachable before.

Three consecutive `run build` calls over an unchanged file failed on the
third. `needsRun` forced a re-run for any `skipped` execution record, but
`skipped` means two different things: a cascade skip (an earlier file in
the batch failed, so this one never executed) and an `unchanged` skip,
which is the recorded outcome of a correct decision. Build 2 skipped and
wrote `unchanged`; build 3 read that as "never ran", re-executed, and hit
"already exists" on any DDL that is not idempotent. The record then read
`failed`, so every later build retried and failed too.

Only `pending` placeholders and cascade skips force a run now. An
`unchanged` skip falls through to the stale check and checksum comparison,
so a genuinely edited file still re-runs.

Deterministic, not intermittent — likely the root cause behind #54.
@damusix
damusix merged commit dd7e387 into master Jul 29, 2026
4 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

Development

Successfully merging this pull request may close these issues.

run build intermittently fails on objects it just created in the same run

1 participant