{"record":{"id":"4f131648334207f5","repo":"astrid-runtime/astrid","slug":"signed-distro-lock-members-do-not-match-distro-tom","errorCode":null,"errorMessage":"signed Distro.lock members do not match Distro.toml declarations","messagePattern":"signed Distro\\.lock members do not match Distro\\.toml declarations","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/init_signed_source.rs","lineNumber":345,"sourceCode":"        })\n        .collect())\n}\n\n/// Require the signed lock to describe exactly the authenticated TOML members.\nfn validate_signed_member_sets(manifest: &DistroManifest, lock: &DistroLock) -> anyhow::Result<()> {\n    if lock.schema_version != manifest.schema_version\n        || lock.distro.id != manifest.distro.id\n        || lock.distro.version != manifest.distro.version\n    {\n        bail!(\"Distro.lock identity does not match the signed Distro.toml\");\n    }\n\n    let declared: HashMap<&str, &DistroCapsule> = manifest\n        .capsules\n        .iter()\n        .map(|capsule| (capsule.name.as_str(), capsule))\n        .collect();\n    anyhow::ensure!(\n        declared.len() == manifest.capsules.len() && lock.capsules.len() == declared.len(),\n        \"signed Distro.lock members do not match Distro.toml declarations\"\n    );\n    for capsule in &lock.capsules {\n        let declared_capsule = declared\n            .get(capsule.name.as_str())\n            .copied()\n            .ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"signed Distro.lock contains undeclared capsule '{}'\",\n                    capsule.name\n                )\n            })?;\n        if capsule.source != declared_capsule.source || capsule.version != declared_capsule.version\n        {\n            bail!(\n                \"signed Distro.lock entry '{}' does not match Distro.toml\",\n                capsule.name","sourceCodeStart":327,"sourceCodeEnd":363,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/init_signed_source.rs#L327-L363","documentation":"validate_signed_member_sets enforces that the signed Distro.lock describes exactly the same set of capsules as the signed Distro.toml: the declared map must have no duplicate names and the lock must contain exactly that many entries. If either check fails, the lock and manifest are inconsistent and the CLI bails rather than trusting partial members.","triggerScenarios":"fetch_signed_manifest -> verify_signed_manifest -> validate_signed_member_sets when: Distro.toml declares duplicate capsule names (declared.len() < manifest.capsules.len()), or Distro.lock lists a different number of capsules than the manifest declares (extra entries in the lock, or capsules declared in TOML but absent from the lock).","commonSituations":"Distro.lock regenerated after editing Distro.toml but the stale lock is still published; hand-editing either file; merging branches where capsules were added in TOML but the lock was not refreshed.","solutions":["Regenerate Distro.lock from the current Distro.toml (re-run the distro lock/seal step) and republish both files","Remove duplicate capsule name declarations in Distro.toml","Diff the capsule name sets in Distro.toml vs Distro.lock to find the missing/extra entry"],"exampleFix":"# before: lock missing capsule\ncapsules = [\"alpha\"]            # toml\ncapsules = [\"alpha\", \"beta\"]    # stale lock\n# after\ncapsules = [\"alpha\"]            # toml\ncapsules = [\"alpha\"]            # regenerated lock","handlingStrategy":"validation","validationCode":"let toml_names: HashSet<_> = manifest.capsules.iter().map(|c| c.name.clone()).collect();\nlet lock_names: HashSet<_> = lock.capsules.iter().map(|c| c.name.clone()).collect();\nanyhow::ensure!(toml_names.len() == manifest.capsules.len(), \"duplicate capsule names in Distro.toml\");\nanyhow::ensure!(toml_names == lock_names, \"Distro.toml and Distro.lock capsule sets differ\");","typeGuard":null,"tryCatchPattern":"match validate_signed_member_sets(manifest, lock) {\n    Err(e) if e.to_string().contains(\"members do not match\") => {\n        eprintln!(\"Stale lock: regenerate and re-seal Distro.lock from current Distro.toml\");\n    }\n    r => r?,\n}","preventionTips":["Regenerate Distro.lock whenever Distro.toml changes; never hand-edit either","Reject duplicate capsule names at manifest authoring time","Diff toml vs lock capsule name sets in CI before sealing"],"tags":["signing","consistency","toml"],"backgroundTag":"schema-validation-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}