Skip to content

Cross-process layout nondeterminism: hashed-collection order leaks into geometry (at least zenuml) #3

Description

@clintecker

Surfaced by the new LinuxGoldenTests (commit that added it): the same source can render a hair differently across process launches, because Swift randomizes the Set/Dictionary hash seed per process and a few layouts iterate a hashed collection in a way that reaches coordinates.

Evidence: running the golden test alone passed (one process), but running it inside the full swift test failed on zenuml (~0.64% of pixels differ) — a different process, different seed. Setting SWIFT_DETERMINISTIC_HASHING=1 makes rendering reproducible and the goldens stable (that's how CI/scripts/test-linux.sh run), which confirms the cause.

Why StabilityTests miss it: StabilityTests.testLayoutIsDeterministicAcrossRuns compares two layout calls within one process (same seed), so hash-order-dependent output looks stable there. The property that actually matters to users — same source → same pixels in two different app launches — isn't covered.

Impact: minor. Visual only (element ordering/positions shift slightly), no correctness effect, and only in the layouts that iterate hashed collections into geometry. The flowchart engine uses model-order tie-breaks throughout and appears unaffected; zenuml is confirmed affected, likely a couple of others.

Fix direction: audit layouts (start with DiagramLayoutZenUML.swift) for Set/Dictionary iteration whose order influences positions, and sort / use model order before iterating — the same discipline the flowchart engine already follows. Then the "deterministic layout" property holds for users, not just under the test's fixed seed, and SWIFT_DETERMINISTIC_HASHING becomes belt-and-suspenders rather than load-bearing.

Note for the sibling Vinculum engine: if its math layout uses hashed collections anywhere glyph positions are derived, it has the same latent issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions