Skip to content

Add a ledger convergence sweep - #172

Merged
rc4l merged 7 commits into
mainfrom
tools/convergence-check
Aug 3, 2026
Merged

Add a ledger convergence sweep#172
rc4l merged 7 commits into
mainfrom
tools/convergence-check

Conversation

@rc4l

@rc4l rc4l commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Add a ledger convergence sweep

The two existing ledger gates check attribution, not arrival.
commit-tracker-check.sh proves the cited sha is reachable;
commit-tracker-overlap.py proves the cited commit touched a file the
upstream commit touched. Both pass on a row that says ported while the
change was never applied -- which is exactly what happened five times
(355dd1c2f, 754c96a54, 3d24f58bf, e29fce695, bf03d0222), every one a
renderer-staircase batch whose commit really did touch the right files.

This asks the missing question, per FILE rather than per commit: does our
copy match upstream's copy at the newest commit we claim to have ported
for that file? Supersession stops being noise by construction -- if a
later commit rewrote those lines they are not in the newest upstream
state either -- and a hunk whose our-side lines carry a fork tag
([rc4l], [BB], ...) is a divergence someone signed for.

SCOPED TO THE STAIRCASE, and that is load-bearing. Run over the whole
tree it flagged 207 of 459 files and led with a 1975-line "divergence" in
dobjtype.cpp against a sweeping "removed STACK_ARGS" commit -- all of it
correct divergence, none actionable, because outside src/gl we port
slices and never intend to converge. Inside the staircase we walk commit
by commit precisely so our copy tracks upstream's, so the question is
meaningful there. That is also where every miss the audit found lived.
Scoped: 129 files, 39 findings, under a second.

Validated against the two known misses rather than assumed: with #171
unmerged it independently reports gl_interface.cpp missing
GL_CONTEXT_PROFILE_MASK and gl_texture.cpp missing the diagonal CHKPIX
checks, and surfaces a third candidate in gl_texture.cpp I had not found
by hand.

Runs weekly rather than per-PR. The other gates read committed snapshots;
this one needs upstream file content, so it needs the clone. A periodic
full pass is also the right shape for the failure it hunts, which is
silent and accumulates.

ADVISORY for now -- 39 findings need triage before a flag can mean
"stop", the same path overlap.py took.

The two existing ledger gates check attribution, not arrival.
commit-tracker-check.sh proves the cited sha is reachable;
commit-tracker-overlap.py proves the cited commit touched a file the
upstream commit touched. Both pass on a row that says ported while the
change was never applied -- which is exactly what happened five times
(355dd1c2f, 754c96a54, 3d24f58bf, e29fce695, bf03d0222), every one a
renderer-staircase batch whose commit really did touch the right files.

This asks the missing question, per FILE rather than per commit: does our
copy match upstream's copy at the newest commit we claim to have ported
for that file? Supersession stops being noise by construction -- if a
later commit rewrote those lines they are not in the newest upstream
state either -- and a hunk whose our-side lines carry a fork tag
([rc4l], [BB], ...) is a divergence someone signed for.

SCOPED TO THE STAIRCASE, and that is load-bearing. Run over the whole
tree it flagged 207 of 459 files and led with a 1975-line "divergence" in
dobjtype.cpp against a sweeping "removed STACK_ARGS" commit -- all of it
correct divergence, none actionable, because outside src/gl we port
slices and never intend to converge. Inside the staircase we walk commit
by commit precisely so our copy tracks upstream's, so the question is
meaningful there. That is also where every miss the audit found lived.
Scoped: 129 files, 39 findings, under a second.

Validated against the two known misses rather than assumed: with #171
unmerged it independently reports gl_interface.cpp missing
GL_CONTEXT_PROFILE_MASK and gl_texture.cpp missing the diagonal CHKPIX
checks, and surfaces a third candidate in gl_texture.cpp I had not found
by hand.

Runs weekly rather than per-PR. The other gates read committed snapshots;
this one needs upstream file content, so it needs the clone. A periodic
full pass is also the right shape for the failure it hunts, which is
silent and accumulates.

ADVISORY for now -- 39 findings need triage before a flag can mean
"stop", the same path overlap.py took.
@rc4l
rc4l enabled auto-merge August 3, 2026 15:13
rc4l added 6 commits August 3, 2026 10:16
A weekly artifact nobody opens is not a gate. Run it from the Actions tab
when closing out a seam or after a batch of staircase ports, which is when
its answer actually changes.
The ADVISORY switch decided an exit code that nothing reads -- the sweep
runs on demand, not in a gate -- so it was a knob with no effect. Gone.

Reviewing the sweep's findings turned up four more rows marked ported
whose code is not in the tree. These are reversed to pending rather than
quietly fixed, because the ledger claiming work that does not exist is
the problem being tracked:

  a8398e70b  FGLROptions never initialises brightfog, so the MAPINFO
             option reads uninitialised memory.
  baa775b31  gl_lightdata.cpp:334 indexes distfogtable[..][lightlevel]
             with no clamp -- out of bounds for a light level outside
             0..255.
  b00b49c11  gl_sky.cpp still picks the skybox by hand instead of
             sector_t::GetSkyBox(plane), which exists here, so the fix
             was portable and simply not taken.
  d79741574  ADynamicLight::SetOrigin has no 'moving' parameter, so the
             relink-on-move fix is absent.

That makes nine ledger errors found in total, all in the renderer
staircase, all the same shape: a batch commit that touched the right
files while one of its changes never landed.
All three were recorded ported and were not in the tree; the convergence
sweep found them and this applies them for real.

a8398e70b  FGLROptions declared brightfog but its constructor never
           initialised it, so the MAPINFO option read whatever was on the
           heap.
baa775b31  gl_lightdata.cpp indexed distfogtable[..][lightlevel] with no
           clamp. The table is [2][256] and lightlevel is not guaranteed
           in range, so this was an out-of-bounds read.
b00b49c11  gl_sky.cpp picked the skybox by hand and fell back to
           level.DefaultSkybox itself. sector_t::GetSkyBox(plane) does
           that fallback and also honours SECF_NOFLOORSKYBOX /
           SECF_NOCEILINGSKYBOX, which the hand-rolled version ignored --
           so this fixed a second bug the commit did not advertise.

d79741574 is NOT here, and my reversing it to pending was wrong.
ADynamicLight::SetOrigin already exists and already calls LinkLight(),
which is the substance of that commit. Only the 'moving' parameter is
absent, and that comes from an earlier upstream change to
AActor::SetOrigin that we never took -- so the row is adapted with the
remainder noted, not pending.
@rc4l
rc4l merged commit d094ae2 into main Aug 3, 2026
9 of 10 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.

1 participant