From 688e1bfde881dabba4528b51bce75342a18c1e00 Mon Sep 17 00:00:00 2001 From: David Meister Date: Sat, 15 Aug 2026 12:52:49 +0000 Subject: [PATCH] One spelling of the record root, pinned to the writer's MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root the frozen record lives in was spelled three times: `dirForSnapshot` concatenated the literal `"src/generated/"`, `LIB_FS_ROOT` restated it, and `LibFs.pathForContract` hardcodes it a third time in a package this repo does not own. A walk of a root nothing writes to returns an empty list, which is also the correct answer for a repo that has released nothing — so a root that drifted out of step would leave every record-anchored assertion passing with no subject, permanently and silently. `dirForSnapshot` now derives from `LIB_FS_ROOT`, which is the only spelling of the root inside this library, and `testRecordRootIsTheRootTheWriterWritesTo` pins that constant against the root `pathForSnapshot` actually writes through. An empty walk therefore means the record is empty rather than misaddressed. Closes #81 Co-Authored-By: Claude Opus 5 (1M context) --- src/abstract/RainDeployVerifySnapshot.sol | 19 +++++++++++++++++++ src/lib/LibRainDeploySnapshot.sol | 17 ++++++++++++----- test/src/lib/LibRainDeploySnapshot.t.sol | 22 ++++++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/abstract/RainDeployVerifySnapshot.sol b/src/abstract/RainDeployVerifySnapshot.sol index 05416d7..e663f10 100644 --- a/src/abstract/RainDeployVerifySnapshot.sol +++ b/src/abstract/RainDeployVerifySnapshot.sol @@ -260,6 +260,25 @@ abstract contract RainDeployVerifySnapshot is RainDeployVerifyBase { /// Every release in the frozen record MUST be declared, so that the set the /// chain group checks is every release this repo has ever cut rather than /// the ones somebody remembered to list. + /// + /// An empty walk passes, and is meant to: that is the state of every deploy + /// repo before its first release. What makes it safe is that the root is + /// the one a snapshot is WRITTEN to — `LIB_FS_ROOT` is the only spelling of + /// it in `LibRainDeploySnapshot` and `testRecordRootIsTheRootTheWriterWritesTo` + /// pins it against `LibFs`'s. Under the writer's own root, finding nothing + /// means there is nothing; under any other, the walk returns an empty list + /// forever, this passes with no subject, and the one check standing between + /// a release dropping out of everything and a green suite is inert. + /// + /// Deliberately NOT also guarded by comparing the record's size against the + /// declaration's. The two are emitted one-for-one by `writeReleasedSuitesLib` + /// for a repo that generates its declaration from its record, but this is + /// inherited by any repo that overrides `releasedSuites`, and a declaration + /// with no record behind it is a state such a repo is legitimately in: a + /// release deployed before it adopted this machinery has no frozen record + /// and never will. A size check would red-line that permanently with no way + /// to spell the exemption, while the release it names goes on being checked + /// by everything anchored to a chain. function testEveryFrozenSnapshotIsReleased() external view { checkFrozenSnapshotsReleased( LibRainDeploySnapshot.frozenSnapshotPaths(vm, LibRainDeploySnapshot.LIB_FS_ROOT), releasedSuites() diff --git a/src/lib/LibRainDeploySnapshot.sol b/src/lib/LibRainDeploySnapshot.sol index c8c684f..6039ea2 100644 --- a/src/lib/LibRainDeploySnapshot.sol +++ b/src/lib/LibRainDeploySnapshot.sol @@ -160,11 +160,22 @@ library LibRainDeploySnapshot { return string(tagBytes); } + /// The output root `LibFs` writes to, and the only one it can write to: + /// `LibFs.pathForContract` hardcodes it. + /// + /// The only spelling of that root in this library. Everything here that + /// names the root — the directory a snapshot is written into, and the tree + /// the frozen record is walked from — reads it, so a root this library + /// walks that is not a root it writes to is not a state it can be in. The + /// remaining pair, this and `LibFs`'s own, is what + /// `testRecordRootIsTheRootTheWriterWritesTo` pins. + string constant LIB_FS_ROOT = "src/generated"; + /// The directory holding a snapshot, rolling or frozen. /// @param dir The snapshot directory name — a release tag, or `CANDIDATE`. /// @return The directory path. function dirForSnapshot(string memory dir) internal pure returns (string memory) { - return string.concat("src/generated/", dir); + return string.concat(LIB_FS_ROOT, "/", dir); } /// The contract name that places a generated file inside a snapshot @@ -189,10 +200,6 @@ library LibRainDeploySnapshot { return LibFs.pathForContract(snapshotName(dir, contractName)); } - /// The output root `LibFs` writes to, and the only one it can write to: - /// `LibFs.pathForContract` hardcodes it. - string constant LIB_FS_ROOT = "src/generated"; - /// Every file in the FROZEN record: everything inside a release-tag /// directory under `root`. /// diff --git a/test/src/lib/LibRainDeploySnapshot.t.sol b/test/src/lib/LibRainDeploySnapshot.t.sol index b917ea4..6a46451 100644 --- a/test/src/lib/LibRainDeploySnapshot.t.sol +++ b/test/src/lib/LibRainDeploySnapshot.t.sol @@ -201,6 +201,28 @@ contract LibRainDeploySnapshotTest is Test { assertEq(LibRainDeploySnapshot.pathForSnapshot("0_1_7", "Foo"), "src/generated/0_1_7/Foo.sol"); } + /// The root the record is WALKED from MUST be the root the writer WRITES + /// to. They are two constants — `LIB_FS_ROOT` here, and the one + /// `LibFs.pathForContract` hardcodes in a package this repo does not own — + /// and a walk of a root nothing writes to returns nothing, which every + /// record-anchored assertion then passes on. Silence is the failure mode, + /// so it is asserted rather than observed. + /// + /// Compared through `pathForSnapshot`, which is what a snapshot is written + /// through, so the right hand side is the writer's own root rather than a + /// third restatement of it. The contract name is arbitrary — the path is + /// built by concatenation and names no artifact. + function testRecordRootIsTheRootTheWriterWritesTo() external pure { + assertEq( + LibRainDeploySnapshot.pathForSnapshot("0_1_7", "Foo"), + string.concat(LibRainDeploySnapshot.LIB_FS_ROOT, "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/0_1_7/Foo.sol") + ); + assertEq( + LibRainDeploySnapshot.dirForSnapshot(LibRainDeploySnapshot.CANDIDATE), + string.concat(LibRainDeploySnapshot.LIB_FS_ROOT, "/", LibRainDeploySnapshot.CANDIDATE) + ); + } + /// A snapshot MUST land at the path this library says it does, and writing /// one over a directory that is already there is the ORDINARY case: the /// rolling snapshot is regenerated into the same `candidate/` on every