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 3 packages that land under dependencies/:
@openzeppelin-contracts 5.6.1forge-std 1.16.1rain-solmem 0.1.3
foundry.lock meanwhile still carries 2 submodule pins, both for a lib/ path that does not exist in the tree:
{
"lib/openzeppelin-contracts": {
"rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565"
},
"lib/rain.solmem": {
"rev": "2e47e41af85a711d837b2518656db0efdae814c2"
}
}Both pins contradict what the build actually uses. fcbae539 is openzeppelin-contracts v5.5.0 while soldeer resolves 5.6.1; 2e47e41 is not any tagged release of rain.solmem (v0.1.3 is ce03de80) while soldeer resolves rain-solmem0.1.3. The file is also incomplete against the real dependency set — it has no entry for forge-std, which is 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 — 2 warnings on every build here, verified in the pinned rainix#sol-shell:
Warning: Dependency 'lib/rain.solmem' not found at expected path
Warning: Dependency 'lib/openzeppelin-contracts' 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 itselfREUSE.toml line 19 — "foundry.lock", in the annotation path list.soldeerignore line 19 — /foundry.lock
Submodule paths that no longer exist
REUSE.toml line 9 — ".gitmodules",. There is no .gitmodules in the tree and git ls-files --stage reports zero gitlinks..soldeerignore line 7 — .gitmodules, same absent file..soldeerignore line 21 — /lib. There is no lib/ directory. foundry.toml sets libs = ["dependencies"], so forge resolves everything under dependencies/ and never creates 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 further dangling REUSE.toml annotation, which also goes
REUSE.toml line 7 — ".vscode/**/". This repo tracks no .vscode/ directory, so the annotation matches nothing.
A misspelled .soldeerignore 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.
Absent, but not residue — this stays
.soldeerignore line 11 — CLAUDE.md. This repo has no CLAUDE.md, tracked or ignored. It is an ordinary file a repo gains later, so the entry is a live forward-looking rule and is left alone.
Deliberately not in scope..soldeerignore also names .DS_Store, .vscode, .pre-commit-config.yaml and the build/publish outputs (/out, /cache, /dependencies, /target, /result, /meta, /docs, /deployments). Those are absent from a clean checkout by design — OS junk, local developer files, or artifacts generated at forge soldeer install / forge build 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, remappings.txt, .gitignore, README.md and the rest of the tree were grepped and are clean. This repo has no CLAUDE.md, so there is no stale prose describing lib/ submodules to fix here.
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. That is why .vscode above goes from REUSE.toml while .vscode stays in .soldeerignore.
REUSE.tomldescribes 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-sollegal 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.string 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 bothfoundry.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
What
foundry.lockisfoundry.lockis Foundry's git submodule lockfile. It records the commit each dependency vendored underlib/is pinned to, soforge install/forge updatecan 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
.gitmodulesand nolib/directory onmain. Dependencies come from soldeer:foundry.tomlsetslibs = ["dependencies"], andsoldeer.lockis the live lockfile for the 3 packages that land underdependencies/:@openzeppelin-contracts5.6.1forge-std1.16.1rain-solmem0.1.3foundry.lockmeanwhile still carries 2 submodule pins, both for alib/path that does not exist in the tree:{ "lib/openzeppelin-contracts": { "rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565" }, "lib/rain.solmem": { "rev": "2e47e41af85a711d837b2518656db0efdae814c2" } }Both pins contradict what the build actually uses.
fcbae539is openzeppelin-contractsv5.5.0while soldeer resolves 5.6.1;2e47e41is not any tagged release of rain.solmem (v0.1.3isce03de80) while soldeer resolvesrain-solmem0.1.3. The file is also incomplete against the real dependency set — it has no entry forforge-std, which is live. Nothing reconciles any of it, because nothing reads thefoundry.lockside.Submodules also cannot come back: rainix CI runs a
no-submodulescheck that fails on a root.gitmodulesor any committed gitlink, so there is no future in which these pins become live again.It is not silent
forge buildemits one warning per entry in this file — 2 warnings on every build here, verified in the pinnedrainix#sol-shell:Everything dangling in this repo
foundry.lockis not the only thing the submodule → soldeer migration left behind. Every reference below names a path that does not exist onmain— but absence on its own is not what makes a reference removable, andREUSE.tomland.soldeerignoredo 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 itselfREUSE.tomlline 19 —"foundry.lock",in the annotationpathlist.soldeerignoreline 19 —/foundry.lockSubmodule paths that no longer exist
REUSE.tomlline 9 —".gitmodules",. There is no.gitmodulesin the tree andgit ls-files --stagereports zero gitlinks..soldeerignoreline 7 —.gitmodules, same absent file..soldeerignoreline 21 —/lib. There is nolib/directory.foundry.tomlsetslibs = ["dependencies"], so forge resolves everything underdependencies/and never createslib/.None of these can come back. rainix CI runs a
no-submodulescheck that fails on a root.gitmodulesor any committed gitlink.A further dangling
REUSE.tomlannotation, which also goesREUSE.tomlline 7 —".vscode/**/". This repo tracks no.vscode/directory, so the annotation matches nothing.A misspelled
.soldeerignoreentry — corrected in place, not dropped.soldeerignoreline 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.yamlis 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.Absent, but not residue — this stays
.soldeerignoreline 11 —CLAUDE.md. This repo has noCLAUDE.md, tracked or ignored. It is an ordinary file a repo gains later, so the entry is a live forward-looking rule and is left alone.Deliberately not in scope.
.soldeerignorealso names.DS_Store,.vscode,.pre-commit-config.yamland the build/publish outputs (/out,/cache,/dependencies,/target,/result,/meta,/docs,/deployments). Those are absent from a clean checkout by design — OS junk, local developer files, or artifacts generated atforge soldeer install/forge buildtime and therefore present whensoldeer pushruns. They are correct ignores and must stay.Nothing else references any of it.
.github/workflows/,flake.nix,foundry.toml,remappings.txt,.gitignore,README.mdand the rest of the tree were grepped and are clean. This repo has noCLAUDE.md, so there is no stale prose describinglib/submodules to fix here.Which of these actually get removed
REUSE.tomland.soldeerignorefail in opposite directions, so the same absent path is residue in one and a live rule in the other. That is why.vscodeabove goes fromREUSE.tomlwhile.vscodestays in.soldeerignore.REUSE.tomldescribes files that exist. An annotation naming a path that is not there describes nothing, so it is residue and it goes.reuse lint(therainix-sollegaljob) 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 trackedsoldeer.lockmakesreuse lintexit 1..soldeerignoreis 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-submodulescheck fails the build on a root.gitmodulesor any committed gitlink,/libis that same vendored-submodule directory, andfoundry.lockis only ever written forlib/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.soldeerignorelines 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.lockline tookforge soldeer push --dry-runfrom 14 entries to 15, withsoldeer.locknow inside the package.A misspelled entry is the one case that is edited rather than deleted, for the same reason:
.coderabbitai.yamlprotects 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.solmemvia soldeer, nine have already droppedfoundry.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.string only.Deleting the file was never the whole job. Every one of those nine left dangling
.gitmodulesand/orlib/references behind — rain.merkle, for instance, deletedfoundry.lockyet still annotates bothfoundry.lockand.gitmodulesinREUSE.toml, and itslegaljob 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.flowandrain.tier.interfacegenuinely still use git submodules, so their.gitmodulesandlib/entries are correct and must be left alone.Done when
foundry.lockdeletedREUSE.tomlentry listed above removed.soldeerignorelines 7 (.gitmodules), 19 (/foundry.lock) and 21 (/lib) removed.soldeerignoreline 2 corrected in place to.coderabbit.yaml, not deleted.soldeerignoreline left exactly as it is,CLAUDE.mdand.vscodeincludedforge buildno longer emitsDependency '...' not found at expected path.gitmodules,lib/orfoundry.lockremains anywhere in the tree outsidedependencies/rainix-soltest / static / legal)