{"record":{"id":"c20a24144414cc0e","repo":"affaan-m/ECC","slug":"candidate-id-collision-with-different-immutable-co","errorCode":null,"errorMessage":"candidate id collision with different immutable content","messagePattern":"candidate id collision with different immutable content","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ecc2/src/session/store.rs","lineNumber":5394,"sourceCode":"                anyhow::bail!(\"legacy candidate id collision with different immutable content\");\n            }\n            let tx = self.conn.unchecked_transaction()?;\n            Self::register_harness_alias(&tx, &candidate.id, &legacy_id)?;\n            tx.commit()?;\n            return Ok(());\n        }\n        self.conn.execute(\n            \"INSERT INTO harness_candidates (id, canonical_config_json, trace_refs_json, evidence_refs_json, created_at)\n             VALUES (?1, ?2, ?3, ?4, ?5) ON CONFLICT(id) DO NOTHING\",\n            rusqlite::params![candidate.id, candidate.canonical_config, trace_json, evidence_json, chrono::Utc::now().to_rfc3339()],\n        )?;\n        let stored: (String, String, String) = self.conn.query_row(\n            \"SELECT canonical_config_json, trace_refs_json, evidence_refs_json FROM harness_candidates WHERE id = ?1\",\n            [&candidate.id],\n            |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),\n        )?;\n        if stored != expected {\n            anyhow::bail!(\"candidate id collision with different immutable content\");\n        }\n        Ok(())\n    }\n\n    pub fn activate_initial_harness(&self, candidate_id: &str, evidence_ref: &str) -> Result<()> {\n        if candidate_id.len() != 64 || evidence_ref.trim().is_empty() || evidence_ref.len() > 4096 {\n            anyhow::bail!(\n                \"valid candidate id and bounded activation evidence reference are required\"\n            );\n        }\n        let tx = self.conn.unchecked_transaction()?;\n        let stored_candidate_id = Self::resolve_harness_candidate_id(&tx, candidate_id)?;\n        if tx\n            .query_row(\n                \"SELECT candidate_id FROM active_harness_config WHERE slot = 'default'\",\n                [],\n                |row| row.get::<_, String>(0),\n            )","sourceCodeStart":5376,"sourceCodeEnd":5412,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/ecc2/src/session/store.rs#L5376-L5412","documentation":"After an INSERT ... ON CONFLICT(id) DO NOTHING into harness_candidates, the store re-reads the stored row and compares it byte-for-byte against the expected canonical config and ref JSON. If a row already existed under the candidate's v2 id with different immutable content, the write is treated as a content-address id collision and the call fails rather than silently overwriting.","triggerScenarios":"Calling the candidate persist/registration API with a candidate whose 64-char v2 id already exists in harness_candidates but whose canonical_config_json, trace_refs_json, or evidence_refs_json differ from the stored tuple.","commonSituations":"A hash/id computation returning the same id for modified candidate content (e.g. after tweaking trace refs or evidence refs); two environments sharing one database with divergent candidate definitions under the same id; replaying an old registration after content changed.","solutions":["Regenerate the candidate id from the current immutable content so a modified candidate gets a fresh id.","Diff the stored canonical_config_json/trace_refs_json/evidence_refs_json against the submitted candidate to identify which field drifted, then make the content match the stored row.","If the pre-existing row is wrong or abandoned, delete it explicitly and re-register the candidate.","Ensure all writers use the same canonicalization (field order, JSON formatting) so identical content yields identical stored tuples."],"exampleFix":"// before: content changed but id reused\nlet candidate = CandidateSpec { id: old_id, trace_refs: new_refs, .. };\nstore.persist_candidate(&candidate)?; // stored != expected -> collision\n\n// after: derive id from current content\nlet id = candidate.id_for_v2()?; // fresh 64-char id reflecting new_refs\nlet candidate = CandidateSpec { id, trace_refs: new_refs, .. };\nstore.persist_candidate(&candidate)?;","handlingStrategy":"validation","validationCode":"fn ensure_id_content_match(store: &Store, candidate: &CandidateSpec) -> anyhow::Result<()> {\n    if let Some(stored) = store.lookup_candidate(&candidate.id)? {\n        anyhow::ensure!(\n            stored.canonical_config_json == candidate.canonical_config\n                && stored.trace_refs_json == serde_json::to_string(&candidate.trace_refs)?\n                && stored.evidence_refs_json == serde_json::to_string(&candidate.evidence_refs)?,\n            \"id already registered with different content\"\n        );\n    }\n    Ok(())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always compute the v2 id via id_for_v2() from current content — never reuse ids across content changes","Use one canonical JSON serialization everywhere so identical content yields identical stored tuples","If registration fails, diff stored vs expected JSON to find the drifted field before retrying"],"tags":["database","content-addressing","collision","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}