Write the optional UNHASHED section. This section is NOT included in the content hash. It's intended for data that shouldn't affect the change's identity: AI transcripts, reasoning traces, editor state, review comments, etc. The payload is provided as raw bytes (typically JSON-serialized). The writer compresses it with zstd but does NOT feed it through the blake3 hasher. # Arguments `data` - T
(&mut self, data: &[u8])
| 170 | /// - Zstd compression errors. |
| 171 | /// - I/O errors. |
| 172 | pub fn write_unhashed(&mut self, data: &[u8]) -> FormatResult<()> { |
| 173 | self.ensure_metadata_complete()?; |
| 174 | self.transition_to_at_least(WriterState::WritingContent)?; |
| 175 | |
| 176 | if self.state == WriterState::WroteUnhashed || self.state == WriterState::Finalized { |
| 177 | return Err(FormatError::UnexpectedSection { |
| 178 | got: "UNHASHED".to_string(), |
| 179 | expected: format!( |
| 180 | "UNHASHED can only be written once, state is {}", |
| 181 | self.state.name() |
| 182 | ), |
| 183 | }); |
| 184 | } |
| 185 | |
| 186 | // Compress the data |
| 187 | let compressed = zstd::encode_all(data, self.options.compression_level()) |
| 188 | .map_err(|e| FormatError::Compress(e.to_string()))?; |
| 189 | |
| 190 | // Write section header + compressed payload |
| 191 | let section_header = SectionHeader::new(SectionType::Unhashed, compressed.len() as u32); |
| 192 | section_header.write_to(self.writer)?; |
| 193 | self.writer.write_all(&compressed)?; |
| 194 | |
| 195 | // NOTE: Do NOT feed through hasher — unhashed section is excluded |
| 196 | // from the content hash by design. |
| 197 | |
| 198 | self.stats.sections_written += 1; |
| 199 | self.stats.total_uncompressed += data.len() as u64; |
| 200 | self.stats.total_compressed += compressed.len() as u64; |
| 201 | self.stats.total_bytes_written += (SectionHeader::SIZE + compressed.len()) as u64; |
| 202 | |
| 203 | self.state = WriterState::WroteUnhashed; |
| 204 | Ok(()) |
| 205 | } |
| 206 | } |