1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! What:
//! How the service adds a review entry to the shadow log of a worktree that people and agents are editing at the same
//! time: append only, never create, never rewrite, once per commit, with a bounded repair of entries another writer
//! overwrote.
//! Why:
//! `doc/audit/standards-review-gate-shadow.md` is a tracked file that sessions edit while the service appends to it, and
//! no lock may be taken. One `O_APPEND` write of the whole entry cannot clobber anyone's content and cannot interleave
//! with another append. What an append cannot prevent is another writer that read the file before it and writes the
//! whole file back after it, which loses the entry. Each entry is therefore also kept as a plain file in the Git
//! directory, and every delivery checks the latest few entries are still in the log and appends the missing ones again.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! if (!(await readFile(log)).includes(sha12)) await appendFile(log, text); await repairRecent(log, spool);
//! ```
use super::private_storage::{create_private_directory_all, write_private_file_unsynced};
use super::standards_review_entry::SHORT_SHA_LENGTH;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
/// Spooled entries the repair checks against the log on each delivery.
pub const REPAIR_WINDOW: usize = 16;
/// Spooled entries kept; older ones are removed so the spool stays small.
pub const SPOOL_KEPT: usize = 4 * REPAIR_WINDOW;
/// What a delivery did.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Delivery {
/// The entry was appended.
Appended,
/// The log already names this commit, so nothing was added.
AlreadyThere,
}
/// What:
/// The spool directory of one worktree under a Git directory: entries of different worktrees are never mixed, because
/// each worktree's log holds the history of its own branch.
/// Why:
/// A worktree on another branch lacks commits that a sibling worktree made, and must not be given their entries.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// join(commonDir, 'cli-git/review-entries', fnv1a(repository))
/// ```
pub fn spool_directory(common_dir: &Path, repository: &str) -> PathBuf {
let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
for byte in repository.bytes() {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(0x0100_0000_01b3);
}
return common_dir.join("cli-git").join("review-entries").join(format!("{hash:016x}"));
}
/// The text the log is searched for: the first characters of the commit name, which survive a reflow of the entry.
fn token(sha: &str) -> String {
return sha.chars().take(SHORT_SHA_LENGTH).collect();
}
/// Whether the log names the commit already; a log that cannot be read names nothing.
pub fn names_commit(log: &Path, sha: &str) -> bool {
return std::fs::read_to_string(log).is_ok_and(|text| return text.contains(token(sha).as_str()));
}
/// One whole-entry append to a log that must already exist.
fn append_existing(log: &Path, text: &str) -> std::io::Result<()> {
let mut file: std::fs::File = std::fs::OpenOptions::new().append(true).create(false).open(log)?;
return file.write_all(text.as_bytes());
}
/// The spooled entries, newest first, as (commit token, path, text).
fn spooled(spool: &Path) -> Vec<(String, PathBuf, String)> {
let mut found: Vec<(SystemTime, String, PathBuf)> = Vec::new();
if let Ok(listing) = std::fs::read_dir(spool) {
for entry in listing.flatten() {
let modified: SystemTime = entry.metadata().and_then(|meta| return meta.modified()).unwrap_or(SystemTime::UNIX_EPOCH);
found.push((modified, entry.file_name().to_string_lossy().into_owned(), entry.path()));
}
}
found.sort_by(|left, right| return right.0.cmp(&left.0).then_with(|| return right.1.cmp(&left.1)));
return found
.into_iter()
.map(|(_, name, path)| return (name, path.clone(), std::fs::read_to_string(path).unwrap_or_default()))
.collect();
}
/// What:
/// Append again the newest spooled entries whose commit the log no longer names, and remove spooled entries beyond
/// `SPOOL_KEPT`. Returns how many entries were appended again.
/// Why:
/// This is the repair for an append another writer overwrote; it looks at a bounded number of entries per call.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// for (const entry of spooled.slice(0, window)) if (!log.includes(entry.token)) await appendFile(log, entry.text);
/// ```
pub fn repair_recent(log: &Path, spool: &Path) -> usize {
let entries: Vec<(String, PathBuf, String)> = spooled(spool);
let mut present: String = std::fs::read_to_string(log).unwrap_or_default();
let mut repaired: usize = 0;
for (name, _, text) in entries.iter().take(REPAIR_WINDOW) {
if text.is_empty() || present.contains(name.as_str()) {
continue;
}
if append_existing(log, text.as_str()).is_ok() {
present.push_str(text.as_str());
repaired += 1;
}
}
for (_, path, _) in entries.iter().skip(SPOOL_KEPT) {
let _removed = std::fs::remove_file(path);
}
return repaired;
}
/// What:
/// Deliver one entry for `sha` to `log`: nothing if the log already names the commit, otherwise keep the entry in the
/// spool, append it, then repair the latest few entries.
/// Why:
/// Keeping the commit-name test first makes a repeated item (a crash after the append, before the item was settled)
/// add nothing.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// await deliver({ log, spool, sha, text })
/// ```
pub fn deliver(log: &Path, spool: Option<&Path>, sha: &str, text: &str) -> Result<Delivery, String> {
let current: String = std::fs::read_to_string(log).map_err(|error| return format!("reading {} failed: {error}", log.display()))?;
let name: String = token(sha);
if current.contains(name.as_str()) {
return Ok(Delivery::AlreadyThere);
}
if let Some(directory) = spool {
create_private_directory_all(directory).map_err(|error| return format!("creating the entry spool failed: {error}"))?;
let kept: std::io::Result<()> = write_private_file_unsynced(directory.join(name.as_str()).as_path(), text.as_bytes());
if let Err(error) = kept && error.kind() != std::io::ErrorKind::AlreadyExists {
return Err(format!("keeping the entry failed: {error}"));
}
}
append_existing(log, text).map_err(|error| return format!("appending to {} failed: {error}", log.display()))?;
if let Some(directory) = spool {
let _repaired = repair_recent(log, directory);
}
return Ok(Delivery::Appended);
}
/// Controls stay out of the release executable.
#[cfg(test)]
#[path = "standards_review_append_tests.rs"]
mod tests;