Skip to content

Remove the submodule-era residue — dead foundry.lock, .gitmodules and lib/ references in a repo with no submodules #258

Description

@thedavidmeister

What foundry.lock is

foundry.lock is Foundry's git submodule lockfile. It records the commit each dependency vendored under lib/ is pinned to, so forge install / forge update can restore identical submodule revisions. It is only meaningful in a repo that vendors dependencies as git submodules.

Why it is dead in this repo

There is no .gitmodules and no lib/ directory on main. Dependencies come from soldeer: foundry.toml sets libs = ['dependencies'], and soldeer.lock is the live lockfile for the 7 packages that land under dependencies/:

  • @openzeppelin-contracts 5.6.1
  • forge-std 1.16.1
  • rain-datacontract 0.1.0
  • rain-deploy 0.1.3
  • rain-sol-codegen 0.1.0
  • rain-solmem 0.1.3
  • rain-string 0.2.0

foundry.lock meanwhile still carries 6 submodule pins, every one of them for a lib/ path that does not exist in the tree:

{
  "lib/forge-std": {
    "rev": "1801b0541f4fda118a10798fd3486bb7051c5dd6"
  },
  "lib/rain.datacontract": {
    "rev": "3bff30782e408386f73b9353c5122e2e0ab24df2"
  },
  "lib/rain.deploy": {
    "rev": "ccfdb378d2ab1ed95461ecf1355682250e487e4c"
  },
  "lib/rain.math.fixedpoint": {
    "rev": "8308cbb6da0e231c6f3437f1861e66eff7ea2b00"
  },
  "lib/rain.sol.codegen": {
    "rev": "dfe95884edfde3d4faf51046dc392aa9221bb0a7"
  },
  "lib/rain.string": {
    "rev": "488f237cd59874e4eb91b5a4f747bd57578fec7f"
  }
}

It is stale in two distinct ways:

  • It contradicts the live pins. 1801b054 is forge-std v1.14.0 while soldeer resolves 1.16.1.
  • It describes a different dependency set. It pins lib/rain.math.fixedpoint, which has no counterpart in soldeer.lock at all, and it has no entry for @openzeppelin-contracts or rain-solmem, which are live.

Nothing reconciles any of it, because nothing reads the foundry.lock side.

Submodules also cannot come back: rainix CI runs a no-submodules check that fails on a root .gitmodules or any committed gitlink, so there is no future in which these pins become live again.

It is not silent

forge build emits one warning per entry in this file — 6 warnings on every build here, verified in the pinned rainix#sol-shell:

Warning: Dependency 'lib/forge-std' not found at expected path
Warning: Dependency 'lib/rain.datacontract' not found at expected path
Warning: Dependency 'lib/rain.deploy' not found at expected path
Warning: Dependency 'lib/rain.math.fixedpoint' not found at expected path
Warning: Dependency 'lib/rain.sol.codegen' not found at expected path
Warning: Dependency 'lib/rain.string' not found at expected path

Everything dangling in this repo

foundry.lock is not the only thing the submodule → soldeer migration left behind. Every reference below names a path that does not exist on main — but absence on its own is not what makes a reference removable, and REUSE.toml and .soldeerignore do not behave the same way. Read Which of these actually get removed below before deleting anything.

The dead lockfile and its references

  • foundry.lock — the file itself
  • REUSE.toml line 30 — "foundry.lock", in the annotation path list
  • .soldeerignore line 22 — /foundry.lock

Submodule paths that no longer exist

  • REUSE.toml line 15 — ".gitmodules",. There is no .gitmodules in the tree and git ls-files --stage reports zero gitlinks.
  • .soldeerignore line 8 — .gitmodules, same absent file.
  • .soldeerignore line 24 — /lib. There is no top-level lib/ directory. foundry.toml sets libs = ['dependencies'], so forge resolves everything under dependencies/ and never creates one. (src/lib/ exists and is unrelated — this pattern is anchored with a leading /, so it only ever matched the repo-root lib/.)

None of these can come back. rainix CI runs a no-submodules check that fails on a root .gitmodules or any committed gitlink.

A misspelled entry — corrected in place, not dropped

  • .soldeerignore line 2 — .coderabbitai.yaml. No such file exists, and it never will: the CodeRabbit config filename is .coderabbit.yaml (as used in raindex, rainlang, rainlang.interface, rain.verify and rain.tofu.erc20-decimals). This is a misspelling, so if a real .coderabbit.yaml is ever added here it will be published to soldeer rather than ignored. Correct the spelling in place. Deleting the line does not fix that — it removes the protection instead of restoring it.

Checked and clean. CLAUDE.md is not stale here. Its lib/... bullets sit under the ### Solidity Layer (src/) heading and correctly describe src/lib/, and line 132 already states dependencies are soldeer-managed (libs = ['dependencies']), "not git submodules". Leave it alone.

Deliberately not in scope. .soldeerignore also names .DS_Store, .pre-commit-config.yaml and the build/publish outputs (/out, /cache, /dependencies, /target, /node_modules, /dist, /docs). Those are absent from a clean checkout by design — OS junk, local developer files, or artifacts generated at forge soldeer install / forge build / cargo build / forge doc time and therefore present when soldeer push runs. They are correct ignores and must stay.

Nothing else references any of it. .github/workflows/, flake.nix, foundry.toml, Cargo.toml, crates/, script/, scripts/, package.json, remappings.txt, .gitignore, README.md and the rest of the tree were grepped and are clean.

Which of these actually get removed

REUSE.toml and .soldeerignore fail in opposite directions, so the same absent path is residue in one and a live rule in the other.

REUSE.toml describes files that exist. An annotation naming a path that is not there describes nothing, so it is residue and it goes. reuse lint (the rainix-sol legal job) tolerates a dangling annotation, which is why these survived; what it does not tolerate is the reverse, dropping the annotation of a file that is there. Proved live in rain.lib.memkv — removing the annotation for the tracked soldeer.lock makes reuse lint exit 1.

.soldeerignore is a publish filter, not a description of the tree. An entry naming an absent path is a forward-looking rule: it is the thing that keeps that path out of the published package if anyone ever adds it. So the test for deleting an entry is impossible, not absent — an entry may only go if the path it names can never exist again.

Three entries pass that test, and they pass it structurally rather than by observation: rainix CI's no-submodules check fails the build on a root .gitmodules or any committed gitlink, /lib is that same vendored-submodule directory, and foundry.lock is only ever written for lib/ submodule dependencies. While that check stands none of the three can be recreated, so their filter entries can never match anything again. Those are genuinely dead, and they are the only .soldeerignore lines this issue removes.

Every other entry — .coderabbit.yaml, CLAUDE.md, /target, .cargo, .envrc, whatever a given repo happens to list — is merely not yet added. Deleting one cleans nothing up; it arms a leak, so that the day someone adds the file it ships inside the published soldeer package. Proved live in rain.lib.memkv — dropping the /soldeer.lock line took forge soldeer push --dry-run from 14 entries to 15, with soldeer.lock now inside the package.

A misspelled entry is the one case that is edited rather than deleted, for the same reason: .coderabbitai.yaml protects nothing today, and dropping it leaves the repo just as unprotected. Correcting the spelling is what closes the hole.

Nothing catches an over-deletion. No CI job reads .soldeerignore, so the mistake goes green and only surfaces later, when someone adds the file and it silently publishes.

Context

This is the tail of an unfinished migration, not a convention.

Across the repos that consume rain.solmem via soldeer, nine have already dropped foundry.lock (raindex, rain.verify, rain.tofu.erc20-decimals, rain.flare, rain.dia, rain.erc4626.words, rainlang, rain.merkle, S01-Issuer/st0x.deploy) and eight still carry one: rain.solmem, rainlang.interface, rain.extrospection, rain.math.float, rain.string, rain.lib.memkv, rain.intorastring, rain.datacontract. This issue covers rain.math.float only.

Deleting the file was never the whole job. Every one of those nine left dangling .gitmodules and/or lib/ references behind — rain.merkle, for instance, deleted foundry.lock yet still annotates both foundry.lock and .gitmodules in REUSE.toml, and its legal job is green. That is why this issue covers the residue as well as the lockfile, and why each of those nine now has an issue of its own.

The same residue exists in a further 17 rainlanguage repos outside that consumer set, 10 of which also still carry a dead foundry.lock. They are out of scope here. flow and rain.tier.interface genuinely still use git submodules, so their .gitmodules and lib/ entries are correct and must be left alone.

Done when

  • foundry.lock deleted
  • every REUSE.toml entry listed above removed
  • .soldeerignore lines 8 (.gitmodules), 22 (/foundry.lock) and 24 (/lib) removed
  • .soldeerignore line 2 corrected in place to .coderabbit.yaml, not deleted
  • every other .soldeerignore line left exactly as it is
  • forge build no longer emits Dependency '...' not found at expected path
  • no reference to .gitmodules, root lib/ or foundry.lock remains anywhere in the tree outside dependencies/
  • CI green (rainix-sol test / static / legal)

Metadata

Metadata

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