Save a provenance graph to the repository. Serializes the graph to disk, registers it in the pristine database with `node_type::PROVENANCE`, and records dependencies on the changes this graph explains. # Returns The content hash of the saved provenance graph.
(
&self,
graph: &atomic_core::change::ProvenanceGraph,
)
| 1054 | /// |
| 1055 | /// The content hash of the saved provenance graph. |
| 1056 | pub fn save_provenance_graph( |
| 1057 | &self, |
| 1058 | graph: &atomic_core::change::ProvenanceGraph, |
| 1059 | ) -> Result<Hash, RepositoryError> { |
| 1060 | use atomic_core::pristine::MutTxnT; |
| 1061 | |
| 1062 | // Save to disk |
| 1063 | let hash = self |
| 1064 | .change_store |
| 1065 | .save_provenance_graph(graph) |
| 1066 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1067 | |
| 1068 | // Register in the graph |
| 1069 | let mut txn = self |
| 1070 | .pristine |
| 1071 | .write_txn() |
| 1072 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1073 | |
| 1074 | let prov_id = txn |
| 1075 | .register_provenance(&hash) |
| 1076 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1077 | |
| 1078 | // Register dependencies: provenance → explained changes |
| 1079 | for change_hash in &graph.changes_explained { |
| 1080 | if let Ok(Some(change_id)) = txn.get_internal(change_hash) { |
| 1081 | txn.put_dep(prov_id, change_id) |
| 1082 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1083 | } |
| 1084 | } |
| 1085 | |
| 1086 | // If chained, register dependency on previous provenance graph too |
| 1087 | if let Some(ref prev_hash) = graph.previous { |
| 1088 | if let Ok(Some(prev_id)) = txn.get_internal(prev_hash) { |
| 1089 | txn.put_dep(prov_id, prev_id) |
| 1090 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1091 | } |
| 1092 | } |
| 1093 | |
| 1094 | // Populate session tables from the provenance graph, for any agent |
| 1095 | // (Sherpa, Claude Code, OpenCode, generic atomic-agent, ...). The |
| 1096 | // write transaction is still open, so this is atomic with the |
| 1097 | // provenance registration above. |
| 1098 | txn.populate_session_tables(prov_id.get(), graph) |
| 1099 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1100 | |
| 1101 | // Maintain the Atomic-native session ledger alongside the provenance |
| 1102 | // registration. The JSON file is a mutable runtime cache; this index |
| 1103 | // connects the external session identity to immutable turn objects. |
| 1104 | let json_path = self |
| 1105 | .dot_dir |
| 1106 | .join("sessions") |
| 1107 | .join(format!("{}.json", graph.session_id)) |
| 1108 | .to_string_lossy() |
| 1109 | .to_string(); |
| 1110 | txn.index_session_turn(&graph.session_id, &json_path, &hash, graph) |
| 1111 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1112 | |
| 1113 | txn.commit() |