Skip to content

Two implementations of the source fingerprint produced four defects in one day #907

Description

@OffgridwithJD

test/lib.sh and test/pytest/pgc_cluster.py each carry an implementation of "what was this binary built from". On 2026-09-09 they produced four defects between them, two in each copy, and not one was found by whoever wrote that copy. All four are fixed; this issue is about the arrangement that produced them.

The four

# defect in found by
1 the digest missed objstore/*.c entirely — a separately built module the top-level Makefile reaches by recursion python @linuxhikerpm (#897)
2 the digest mixed in each file's bare name, so src/module.c and objstore/module.c were interchangeable python me, while fixing 1
3 xargs -0 cat | md5sum — no paths, no boundaries, so moving bytes between files left the hash unchanged shell @linuxhikerpm (#898)
4 the digest missed each build directory's Makefile python me, while writing the twin

Defect 3 meant source that cannot compile reported matches the binary under test:

before_hash=bfce474cc159   after_hash=bfce474cc159
initial_compile=0   repartitioned_compile=1   error: redefinition of 'x'

Defect 4 meant editing objstore/Makefile — which changes how that module builds — moved one hash and not the other:

baseline                    shell=45be41a5c47b  python=bea88c7d79ca
objstore/Makefile edited    shell=cfb8f4553041  python=bea88c7d79ca

What makes this an arrangement problem rather than four bugs

The Python docstring said "the same input set as pgc_source_fingerprint in test/lib.sh" throughout all four. It was false when written and stayed false through two rounds of fixing. A prose assertion of agreement is not a mechanism, and it is worse than saying nothing, because it is exactly what stops the next person checking.

Nobody caught their own copy. Four defects, four found by the other party. That is not a comment on either of us; it is what two implementations of one idea do. You cannot review a divergence you are looking at from only one side.

And the failure is silent by construction. When the two disagree the stamp is written to one path and read from another, the reader finds nothing, the verdict is unknown — and unknown is deliberately not a failure, because someone who ran make install by hand has no stamp. So the whole mechanism goes quiet and every suite prints freshness UNVERIFIED while looking healthy. That design decision is right, and it is what makes drift between the two copies undetectable rather than loud.

What is already in place

test/selftest/340 now has an arm asserting the property the docstring only claimed: not that the two hashes are equal — they are different digests over the same files, used independently, and requiring equality would couple two things that have no reason to be coupled — but that the same edit moves both. It walks five edits: a source, a module source, a module Makefile, the top-level Makefile, and the control file.

That arm is a smoke alarm, not a fix. It catches the next divergence; it does not remove the ability to diverge.

The options, and what I would pick

A. Drive the shell from Python, as build_and_install already does. pgc_cluster.build_and_install runs bash -c '. test/lib.sh; pgc_build_and_install …' rather than reimplementing the build, and it does so for exactly this reason, stated in its own docstring. source_fingerprint could do the same and become four lines. This is what I would do. One implementation, one place to be wrong, and the Python side keeps a typed return.

The cost is a bash subprocess per fingerprint. build_once calls it twice per session, so that is measurable in microseconds against a build.

B. Keep both and keep the agreement arm. Cheaper today, and it is where we are. It leaves the ability to diverge in place and relies on a guard nobody has to run.

C. Move the fingerprint out of both into a small script both call. More work than A for the same result, unless something outside test/ needs it.

Why now rather than later

test/pytest/ is on main and jd's rule of 2026-09-09 means every new test ships in both harnesses. Every twin written from here is a chance for a second implementation of something to appear. This is the first one, it produced four defects in a day, and the pattern generalises: the same issue exists in miniature for the 17 fixture constants duplicated across #902's twin, which is a separate discussion but the same shape.

Happy to do A. Wanted the decision recorded rather than made inside a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions