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,
)
| 1038 | /// |
| 1039 | /// The content hash of the saved provenance graph. |
| 1040 | pub fn save_provenance_graph( |
| 1041 | &self, |
| 1042 | graph: &atomic_core::change::ProvenanceGraph, |
| 1043 | ) -> Result<Hash, RepositoryError> { |
| 1044 | use atomic_core::pristine::MutTxnT; |
| 1045 | |
| 1046 | // Save to disk |
| 1047 | let hash = self |
| 1048 | .change_store |
| 1049 | .save_provenance_graph(graph) |
| 1050 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1051 | |
| 1052 | // Register in the graph |
| 1053 | let mut txn = self |
| 1054 | .pristine |
| 1055 | .write_txn() |
| 1056 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1057 | |
| 1058 | let prov_id = txn |
| 1059 | .register_provenance(&hash) |
| 1060 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1061 | |
| 1062 | // Register dependencies: provenance → explained changes |
| 1063 | for change_hash in &graph.changes_explained { |
| 1064 | if let Ok(Some(change_id)) = txn.get_internal(change_hash) { |
| 1065 | txn.put_dep(prov_id, change_id) |
| 1066 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1067 | } |
| 1068 | } |
| 1069 | |
| 1070 | // If chained, register dependency on previous provenance graph too |
| 1071 | if let Some(ref prev_hash) = graph.previous { |
| 1072 | if let Ok(Some(prev_id)) = txn.get_internal(prev_hash) { |
| 1073 | txn.put_dep(prov_id, prev_id) |
| 1074 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1075 | } |
| 1076 | } |
| 1077 | |
| 1078 | // Populate session tables from the provenance graph, for any agent |
| 1079 | // (Sherpa, Claude Code, OpenCode, generic atomic-agent, ...). The |
| 1080 | // write transaction is still open, so this is atomic with the |
| 1081 | // provenance registration above. |
| 1082 | txn.populate_session_tables(prov_id.get(), graph) |
| 1083 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1084 | |
| 1085 | // Maintain the Atomic-native session ledger alongside the provenance |
| 1086 | // registration. The JSON file is a mutable runtime cache; this index |
| 1087 | // connects the external session identity to immutable turn objects. |
| 1088 | let json_path = self |
| 1089 | .dot_dir |
| 1090 | .join("sessions") |
| 1091 | .join(format!("{}.json", graph.session_id)) |
| 1092 | .to_string_lossy() |
| 1093 | .to_string(); |
| 1094 | txn.index_session_turn(&graph.session_id, &json_path, &hash, graph) |
| 1095 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 1096 | |
| 1097 | txn.commit() |