Skip to main content

buntrail_services/git/
worktree_proof.rs

1//! Prepared worktree changes retain the original request and authenticated save history.
2use super::{
3    LocalGitService,
4    observation::{ObservationInput, ObservationOwner, ObservationRecord},
5    observation_store::{Cursor, Plan, Spool},
6};
7use crate::{
8    files::{absent_structure, io_error, save_request_digest, target_path},
9    operations::ledger::SaveObservation,
10};
11use buntrail_core::contracts::*;
12use sha2::{Digest, Sha256};
13use std::{
14    io::Read,
15    sync::{
16        Arc,
17        atomic::{AtomicBool, Ordering},
18    },
19    time::Instant,
20};
21
22fn refused(message: &str) -> CoreError {
23    CoreError::new(ErrorCode::StaleRevision, message)
24}
25fn check_proof(cancel: &AtomicBool, started: Instant) -> Result<(), CoreError> {
26    if cancel.load(Ordering::SeqCst) || started.elapsed() >= super::process::DEADLINE {
27        Err(CoreError::new(
28            ErrorCode::Cancelled,
29            "proof verification cancelled or timed out",
30        ))
31    } else {
32        Ok(())
33    }
34}
35fn request_digest(original: &GitMutationRequest) -> Result<[u8; 32], CoreError> {
36    super::observation::serialized_digest(original)
37}
38#[derive(serde::Serialize, serde::Deserialize)]
39struct Evidence {
40    operation: OperationID,
41    file: FileRef,
42    before: Option<SaveObservation>,
43    after: SaveObservation,
44    captured_absence: bool,
45}
46fn read_evidence(reader: &mut impl Read) -> Result<Option<Evidence>, CoreError> {
47    let mut size = [0; 4];
48    if reader.read(&mut size[..1]).map_err(io_error)? == 0 {
49        return Ok(None);
50    }
51    reader.read_exact(&mut size[1..]).map_err(io_error)?;
52    let size = u32::from_be_bytes(size) as usize;
53    if size > 128 * 1024 {
54        return Err(refused(
55            "save evidence exceeds existing ledger record bound",
56        ));
57    }
58    let mut bytes = vec![0; size];
59    reader.read_exact(&mut bytes).map_err(io_error)?;
60    serde_json::from_slice(&bytes).map(Some).map_err(io_error)
61}
62fn read_approval(
63    reader: &mut impl Read,
64    owner: &ObservationOwner,
65) -> Result<Option<GitSaveApproval>, CoreError> {
66    let mut bytes = [0; 96];
67    if reader.read(&mut bytes[..1]).map_err(io_error)? == 0 {
68        return Ok(None);
69    }
70    reader.read_exact(&mut bytes[1..]).map_err(io_error)?;
71    Ok(Some(GitSaveApproval {
72        operation: OperationID(bytes[..16].try_into().unwrap()),
73        request_digest: bytes[16..48].try_into().unwrap(),
74        actor: WorkspaceLease {
75            client: ClientID(bytes[48..64].try_into().unwrap()),
76            workspace: WorkspaceID(bytes[64..80].try_into().unwrap()),
77            lease: WorkspaceLeaseID(bytes[80..96].try_into().unwrap()),
78            runtime: owner.registration().runtime.clone(),
79        },
80    }))
81}
82fn cursor(spool: &Arc<Spool>) -> Cursor {
83    Cursor {
84        spool: spool.clone(),
85        offset: 0,
86    }
87}
88fn approval_stream(plan: &Plan, additional: Option<&Arc<Spool>>) -> impl Read {
89    let tail: Box<dyn Read> = match additional {
90        Some(spool) => Box::new(cursor(spool)),
91        None => Box::new(std::io::empty()),
92    };
93    plan.approvals().chain(tail)
94}
95fn endpoint(record: &ObservationRecord, version: &FileVersion) -> bool {
96    record.regular.as_ref().is_some_and(|regular| {
97        regular.canonical.as_slice() == version.opaque_token.as_slice()
98            && regular.content_digest == version.content_digest
99    })
100}
101fn valid_endpoint(endpoint: &SaveObservation, target: &TargetIdentity) -> bool {
102    endpoint.requested.target == *target
103        && endpoint.canonical.target == *target
104        && endpoint.requested.opaque_token.len() == 32
105        && endpoint.canonical.opaque_token.len() == 32
106        && endpoint.requested.file_identity.len() == 16
107        && endpoint.requested.file_identity == endpoint.canonical.file_identity
108        && endpoint.requested.content_digest == endpoint.canonical.content_digest
109}
110
111#[derive(Debug)]
112pub enum WorktreePreparation {
113    Existing(Box<OperationReceipt>),
114    Prepared([u8; 16]),
115}
116
117impl LocalGitService {
118    pub fn save_approval_input(
119        &self,
120        owner: &ObservationOwner,
121        approvals: impl IntoIterator<Item = GitSaveApproval>,
122    ) -> Result<ObservationInput, CoreError> {
123        let mut writer = self.observation_input(owner)?;
124        for approval in approvals {
125            if approval.actor.client != owner.registration().client
126                || approval.actor.runtime != owner.registration().runtime
127            {
128                return Err(CoreError::new(
129                    ErrorCode::TargetChanged,
130                    "save approval client or runtime changed",
131                ));
132            }
133            for bytes in [
134                approval.operation.0.as_slice(),
135                approval.request_digest.as_slice(),
136                approval.actor.client.0.as_slice(),
137                approval.actor.workspace.0.as_slice(),
138                approval.actor.lease.0.as_slice(),
139            ] {
140                writer.append(bytes)?;
141            }
142        }
143        writer.seal()
144    }
145    pub fn observation_alias_digest(
146        &self,
147        owner: &ObservationOwner,
148        observation: [u8; 16],
149    ) -> Result<[u8; 32], CoreError> {
150        let mut reader = self.observation_reader(owner, observation)?;
151        let mut hash = Sha256::new();
152        hash.update(b"buntrail.original-aliases.v1\0");
153        while let Some(record) = reader.next_record()? {
154            if let Some(identity) = record.file_reference {
155                hash.update(identity);
156            }
157        }
158        Ok(hash.finalize().into())
159    }
160    pub fn prepare_worktree_sync(
161        &self,
162        owner: &ObservationOwner,
163        original: &GitMutationRequest,
164        observation: [u8; 16],
165        alias_digest: [u8; 32],
166        approvals: ObservationInput,
167        cancel: &AtomicBool,
168    ) -> Result<WorktreePreparation, CoreError> {
169        if let Some(receipt) = self.existing_receipt_sync(original)? {
170            return Ok(WorktreePreparation::Existing(Box::new(receipt)));
171        }
172        #[cfg(test)]
173        if let Some(checkpoint) = &self.after_proof_lookup {
174            checkpoint();
175        }
176        let mutex = super::mutations::repository_mutex(&original.worktree);
177        let _repository = mutex.lock().map_err(io_error)?;
178        if let Some(receipt) = self.existing_receipt_sync(original)? {
179            return Ok(WorktreePreparation::Existing(Box::new(receipt)));
180        }
181        if !original.command.changes_worktree() {
182            return Err(refused("only worktree changes use prepared execution"));
183        }
184        let store = self.observation_store()?;
185        let pin = self.pin_observation(owner, observation)?;
186        let (worktree, state) = pin.original_state()?;
187        if worktree != &original.worktree
188            || state != &original.expected
189            || self.observation_alias_digest(owner, observation)? != alias_digest
190        {
191            return Err(refused(
192                "request differs from its original owned observation or aliases",
193            ));
194        }
195        let _work = store.verification()?;
196        let plan = store.adopt_plan(owner, pin, original, alias_digest, approvals)?;
197        let result = (|| {
198            let evidence = self.freeze_evidence(owner, &plan, None, cancel)?;
199            self.verify_worktree(&plan, &evidence, cancel)?;
200            store.plan(owner, plan.id)?;
201            Ok(WorktreePreparation::Prepared(plan.id))
202        })();
203        if result.is_err() {
204            store.cancel_plan(owner, plan.id);
205        }
206        result
207    }
208    pub fn execute_worktree_sync(
209        &self,
210        owner: &ObservationOwner,
211        original: GitMutationRequest,
212        plan_id: [u8; 16],
213        alias_digest: [u8; 32],
214        approvals: ObservationInput,
215        cancel: &AtomicBool,
216    ) -> Result<OperationReceipt, CoreError> {
217        if let Some(receipt) = self.existing_receipt_sync(&original)? {
218            return Ok(receipt);
219        }
220        #[cfg(test)]
221        if let Some(checkpoint) = &self.after_proof_lookup {
222            checkpoint();
223        }
224        let digest = request_digest(&original)?;
225        let operation = original.operation;
226        let store = self.observation_store()?;
227        let mut input = Some(approvals);
228        let mut execution = None;
229        let mut verify = |accepted: bool| {
230            if !accepted {
231                let plan = store.plan(owner, plan_id)?;
232                if plan.request_digest != digest
233                    || plan.operation != operation
234                    || plan.alias_digest != alias_digest
235                {
236                    return Err(refused(
237                        "prepared original request or alias identity changed",
238                    ));
239                }
240                let work = store.verification()?;
241                let additional = store.adopt_execution_input(
242                    owner,
243                    input
244                        .take()
245                        .ok_or_else(|| refused("execution input already consumed"))?,
246                )?;
247                let evidence = self.freeze_evidence(owner, &plan, Some(&additional), cancel)?;
248                let expected = self.verify_worktree(&plan, &evidence, cancel)?;
249                store.plan(owner, plan_id)?;
250                execution = Some((plan, evidence, work));
251                Ok(expected)
252            } else {
253                let (plan, evidence, _) = execution
254                    .as_ref()
255                    .ok_or_else(|| refused("missing accepted proof snapshot"))?;
256                self.verify_worktree(plan, evidence, cancel)
257            }
258        };
259        let expected = original.expected.clone();
260        let authorize = || store.plan(owner, plan_id).map(|_| ());
261        let receipt = self.mutate_guarded(
262            original,
263            expected,
264            cancel,
265            Some(super::mutations::WorktreeProofGuard {
266                owner,
267                verify: &mut verify,
268                authorize: &authorize,
269            }),
270        );
271        store.cancel_plan(owner, plan_id);
272        receipt
273    }
274    pub fn cancel_worktree_plan(&self, owner: &ObservationOwner, plan: [u8; 16]) {
275        if let Ok(store) = self.observation_store() {
276            store.cancel_plan(owner, plan);
277        }
278    }
279
280    fn freeze_evidence(
281        &self,
282        owner: &ObservationOwner,
283        plan: &Plan,
284        additional: Option<&Arc<Spool>>,
285        cancel: &AtomicBool,
286    ) -> Result<Arc<Spool>, CoreError> {
287        let started = Instant::now();
288        let mut writer = self.observation_input(owner)?;
289        let mut approvals = approval_stream(plan, additional);
290        let mut seen = 0u64;
291        while let Some(approval) = read_approval(&mut approvals, owner)? {
292            check_proof(cancel, started)?;
293            let mut prior = approval_stream(plan, additional);
294            for _ in 0..seen {
295                check_proof(cancel, started)?;
296                if read_approval(&mut prior, owner)?
297                    .is_some_and(|old| old.operation == approval.operation)
298                {
299                    return Err(refused("duplicate approved Save operation"));
300                }
301            }
302            seen += 1;
303            let record = {
304                let ledger = self.files.ledger.lock().map_err(io_error)?;
305                let _lock = ledger.lock()?;
306                let record = ledger
307                    .read(approval.operation)?
308                    .ok_or_else(|| refused("approved Save history is missing"))?;
309                if ledger.expired(&record) {
310                    return Err(refused("approved Save history expired"));
311                }
312                record
313            };
314            let proof = record
315                .save_proof
316                .as_ref()
317                .ok_or_else(|| refused("Save has no authenticated history"))?;
318            let after = proof
319                .after
320                .as_ref()
321                .ok_or_else(|| refused("Save has no durable historical after"))?;
322            let ExpectedState::File { file, version } = &record.envelope.expected else {
323                return Err(refused("Save expected state is malformed"));
324            };
325            let OperationState::Confirmed(MutationResult::Saved(saved)) = &record.receipt.state
326            else {
327                return Err(refused("Save is not durably confirmed"));
328            };
329            let destination = record
330                .destination
331                .as_ref()
332                .ok_or_else(|| refused("Save has no original destination"))?;
333            let resolution_token: [u8; 32] =
334                Sha256::digest(serde_json::to_vec(destination).map_err(io_error)?).into();
335            if record.envelope.kind != MutationKind::Save
336                || record.envelope.target != owner.target
337                || file.target != owner.target
338                || record.envelope.request_digest != approval.request_digest
339                || proof.actor != approval.actor
340                || proof.actor.client != owner.registration().client
341                || proof.actor.runtime != owner.registration().runtime
342                || saved.saved_revision != record.revision
343                || saved.version != after.requested
344                || !valid_endpoint(after, &owner.target)
345                || after.requested.content_digest != record.requested_digest
346                || after.resolved != destination.resolved
347                || after.resolution_token != resolution_token
348                || save_request_digest(file, version, record.revision, record.requested_digest)?
349                    != approval.request_digest
350            {
351                return Err(refused(
352                    "approved Save identity or historical result differs",
353                ));
354            }
355            match (&proof.before, version) {
356                (Some(before), Some(expected))
357                    if before.requested == *expected
358                        && valid_endpoint(before, &owner.target)
359                        && before.resolved == after.resolved
360                        && before.resolution_token == after.resolution_token => {}
361                (None, None) => {}
362                _ => return Err(refused("Save before/after chain is malformed")),
363            }
364            let mut original = plan.original();
365            let mut covered = false;
366            let mut captured_absence = false;
367            while let Some(node) = ObservationRecord::read(&mut original)? {
368                check_proof(cancel, started)?;
369                if node.path != file.path
370                    || node
371                        .membership
372                        .as_ref()
373                        .is_some_and(|membership| !membership.affected)
374                {
375                    continue;
376                }
377                if node.membership.is_some() {
378                    // Remote Save omits hints; original alias identity remains independently pinned.
379                    if node.file_reference.is_none()
380                        || (file.resolved_identity.is_some() && !node.matches_alias(file)?)
381                    {
382                        continue;
383                    }
384                } else if file.resolved_identity.is_some() {
385                    continue;
386                }
387                if node.resolved.as_ref() != Some(&after.resolved) {
388                    continue;
389                }
390                if node.regular.is_none()
391                    && node.structural_token != absent_structure(&Some(destination.clone()))?
392                {
393                    continue;
394                }
395                if node
396                    .regular
397                    .as_ref()
398                    .is_some_and(|regular| regular.resolution != resolution_token)
399                {
400                    continue;
401                }
402                covered = true;
403                captured_absence = node.regular.is_none();
404                break;
405            }
406            if !covered {
407                return Err(refused(
408                    "Save requested alias was not covered by the original observation",
409                ));
410            }
411            let evidence = Evidence {
412                operation: approval.operation,
413                file: file.clone(),
414                before: proof.before.clone(),
415                after: after.clone(),
416                captured_absence,
417            };
418            let bytes = serde_json::to_vec(&evidence).map_err(io_error)?;
419            if bytes.len() > 128 * 1024 {
420                return Err(refused("Save evidence exceeds its bounded durable record"));
421            }
422            writer.append(&(bytes.len() as u32).to_be_bytes())?;
423            writer.append(&bytes)?;
424        }
425        self.observation_store()?
426            .adopt_execution_input(owner, writer.seal()?)
427    }
428
429    fn compare_node(
430        &self,
431        original: &ObservationRecord,
432        current: &ObservationRecord,
433        current_absence: [u8; 32],
434        evidence: &Arc<Spool>,
435        cancel: &AtomicBool,
436        started: Instant,
437    ) -> Result<(), CoreError> {
438        let unaffected = original
439            .membership
440            .as_ref()
441            .is_some_and(|membership| !membership.affected);
442        if original.path != current.path
443            || original.resolved != current.resolved
444            || original.membership != current.membership
445            || original.file_reference != current.file_reference
446        {
447            return Err(refused("original path, alias or membership changed"));
448        }
449        if unaffected {
450            return if original.structural_token == current.structural_token {
451                Ok(())
452            } else {
453                Err(refused("unaffected alias structure changed"))
454            };
455        }
456        // shortcut: scan evidence per node; add a byte-accounted disk index if measured deadline costs require it.
457        let mut reader = cursor(evidence);
458        let mut last: Option<Evidence> = None;
459        let mut matches = 0;
460        while let Some(save) = read_evidence(&mut reader)? {
461            check_proof(cancel, started)?;
462            if original.resolved.as_ref() != Some(&save.after.resolved) {
463                continue;
464            }
465            if let Some(previous) = &last {
466                if save
467                    .before
468                    .as_ref()
469                    .is_none_or(|before| before.canonical != previous.after.canonical)
470                {
471                    return Err(refused("approved canonical Save chain is not contiguous"));
472                }
473            } else if save
474                .before
475                .as_ref()
476                .is_some_and(|before| endpoint(original, &before.canonical))
477                || (save.before.is_none()
478                    && original.regular.is_none()
479                    && original.structural_token == current_absence)
480            {
481                matches += 1;
482            }
483            if endpoint(original, &save.after.canonical) {
484                matches += 1;
485            }
486            last = Some(save);
487        }
488        let Some(last) = last else {
489            return if original == current {
490                Ok(())
491            } else {
492                Err(refused(
493                    "unapproved node changed since original observation",
494                ))
495            };
496        };
497        if matches != 1 || !endpoint(current, &last.after.canonical) {
498            return Err(refused(
499                "original or final node is not an exact Save-chain boundary",
500            ));
501        }
502        if let Some(regular) = &original.regular {
503            if original.structural_token != current.structural_token
504                || regular.resolution != current.regular.as_ref().unwrap().resolution
505            {
506                return Err(refused("saved node structure or resolution changed"));
507            }
508        } else if original.structural_token != current_absence {
509            return Err(refused("created node lost its original absence structure"));
510        }
511        if last.file.path == current.path
512            && current.regular.as_ref().unwrap().requested.as_slice()
513                != last.after.requested.opaque_token.as_slice()
514        {
515            return Err(refused(
516                "saved requested endpoint differs from historical after",
517            ));
518        }
519        Ok(())
520    }
521
522    fn verify_worktree(
523        &self,
524        plan: &Plan,
525        evidence: &Arc<Spool>,
526        cancel: &AtomicBool,
527    ) -> Result<GitExpectedState, CoreError> {
528        let worktree = &plan.pin.data.worktree;
529        let root = self.validate(worktree, cancel)?;
530        let started = Instant::now();
531        let mut reader = plan.original();
532        let mut next = ObservationRecord::read(&mut reader)?;
533        let (state, _) = self.state_records(&root, cancel, |name, node| {
534            use std::os::unix::ffi::OsStrExt;
535            let current = ObservationRecord::from_node(
536                target_path(&root.join(std::ffi::OsStr::from_bytes(name))),
537                node,
538                None,
539            )?;
540            if next.as_ref().is_some_and(|original| {
541                original.membership.is_none() && original.path == current.path
542            }) {
543                self.compare_node(
544                    next.as_ref().unwrap(),
545                    &current,
546                    absent_structure(&node.destination)?,
547                    evidence,
548                    cancel,
549                    started,
550                )?;
551                next = ObservationRecord::read(&mut reader)?;
552            } else {
553                let mut approved = cursor(evidence);
554                let mut created = false;
555                let mut final_matches = false;
556                while let Some(save) = read_evidence(&mut approved)? {
557                    check_proof(cancel, started)?;
558                    if save.after.resolved == current.path {
559                        created |= save.before.is_none() && save.captured_absence;
560                        final_matches = endpoint(&current, &save.after.canonical);
561                    }
562                }
563                if !created || !final_matches {
564                    return Err(refused("unexpected newly enumerated Git path"));
565                }
566            }
567            Ok(())
568        })?;
569        if state.head != plan.pin.data.expected.head
570            || state.index_fingerprint != plan.pin.data.expected.index_fingerprint
571        {
572            return Err(refused("original HEAD or index changed"));
573        }
574        while let Some(original) = next {
575            if original.membership.is_none() {
576                return Err(refused("original Git path disappeared"));
577            }
578            let (mut current, destination) = self.alias_node_record(
579                worktree,
580                FileRef {
581                    target: worktree.target.clone(),
582                    path: original.path.clone(),
583                    resolved_identity: None,
584                },
585                cancel,
586                started,
587            )?;
588            current.file_reference = original.file_reference;
589            self.compare_node(
590                &original,
591                &current,
592                absent_structure(&destination)?,
593                evidence,
594                cancel,
595                started,
596            )?;
597            next = ObservationRecord::read(&mut reader)?;
598        }
599        if self.state(&root, cancel)? != state {
600            return Err(refused("worktree changed during proof validation"));
601        }
602        check_proof(cancel, started)?;
603        Ok(state)
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610    use crate::{connections::local::LocalConnection, files::target_path};
611    use std::{path::Path, sync::Arc};
612    #[test]
613    fn proof_scan_cannot_run_past_the_existing_git_deadline() {
614        assert_eq!(
615            check_proof(
616                &AtomicBool::new(false),
617                Instant::now() - std::time::Duration::from_secs(120)
618            )
619            .unwrap_err()
620            .code,
621            ErrorCode::Cancelled
622        );
623        assert!(check_proof(&AtomicBool::new(false), Instant::now()).is_ok());
624    }
625    fn git(root: &Path, args: &[&str]) {
626        let output = std::process::Command::new("git")
627            .arg("-C")
628            .arg(root)
629            .args(args)
630            .env("GIT_CONFIG_NOSYSTEM", "1")
631            .env("GIT_CONFIG_GLOBAL", "/dev/null")
632            .output()
633            .unwrap();
634        assert!(
635            output.status.success(),
636            "{}",
637            String::from_utf8_lossy(&output.stderr)
638        );
639    }
640    struct Fixture {
641        dir: tempfile::TempDir,
642        local: Arc<LocalConnection>,
643        owner: ObservationOwner,
644        worktree: WorktreeIdentity,
645        file: FileRef,
646    }
647    impl Fixture {
648        fn new() -> Self {
649            let dir = tempfile::tempdir().unwrap();
650            let root = dir.path().join("repo");
651            std::fs::create_dir(&root).unwrap();
652            git(&root, &["init", "-b", "main"]);
653            git(&root, &["config", "user.name", "Fixture"]);
654            git(&root, &["config", "user.email", "fixture@example.invalid"]);
655            std::fs::write(root.join("file"), "original").unwrap();
656            git(&root, &["add", "file"]);
657            git(&root, &["commit", "-m", "initial"]);
658            git(&root, &["branch", "other"]);
659            let local = LocalConnection::open(dir.path().join("state")).unwrap();
660            let lease = WorkspaceLeaseID([1; 16]);
661            local.register(WorkspaceID([2; 16]), lease).unwrap();
662            let owner = local.observation_owner(lease).unwrap();
663            let file = FileRef {
664                target: local.identity.clone(),
665                path: target_path(&root.join("file")),
666                resolved_identity: None,
667            };
668            let worktree = local
669                .git
670                .discover_sync(
671                    FileRef {
672                        path: target_path(&root),
673                        ..file.clone()
674                    },
675                    &AtomicBool::new(false),
676                )
677                .unwrap()
678                .unwrap();
679            Self {
680                dir,
681                local: Arc::new(local),
682                owner,
683                worktree,
684                file,
685            }
686        }
687        fn save(&self, file: FileRef, id: u8, bytes: &[u8]) -> GitSaveApproval {
688            self.save_as(self.owner.registration().lease, file, id, bytes)
689        }
690        fn save_as(
691            &self,
692            lease: WorkspaceLeaseID,
693            file: FileRef,
694            id: u8,
695            bytes: &[u8],
696        ) -> GitSaveApproval {
697            let expected_version = self
698                .local
699                .files
700                .read_sync(file.clone())
701                .ok()
702                .map(|snapshot| snapshot.version);
703            let save = self
704                .local
705                .owned_files(lease)
706                .unwrap()
707                .prepare(SaveRequest {
708                    operation: OperationID([id; 16]),
709                    file,
710                    expected_version,
711                    submitted_revision: BufferRevision(id as u64),
712                    source: ByteSource::Memory(Arc::from(bytes)),
713                })
714                .unwrap();
715            let approval = save.approval().clone();
716            assert!(matches!(save.execute().state, OperationState::Confirmed(_)));
717            approval
718        }
719        fn capture(&self, aliases: Vec<FileRef>) -> (GitMutationRequest, [u8; 16], [u8; 32]) {
720            let retained = self
721                .local
722                .git
723                .observe_owned_sync(
724                    &self.owner,
725                    self.worktree.clone(),
726                    aliases.into_iter().map(Ok),
727                    &AtomicBool::new(false),
728                )
729                .unwrap();
730            let digest = self
731                .local
732                .git
733                .observation_alias_digest(&self.owner, retained.id)
734                .unwrap();
735            (
736                GitMutationRequest {
737                    operation: OperationID([90; 16]),
738                    worktree: self.worktree.clone(),
739                    expected: retained.snapshot.state,
740                    command: GitMutation::Checkout {
741                        reference: b"other".to_vec(),
742                    },
743                },
744                retained.id,
745                digest,
746            )
747        }
748        fn execute(
749            &self,
750            original: GitMutationRequest,
751            observation: [u8; 16],
752            digest: [u8; 32],
753            approvals: Vec<GitSaveApproval>,
754        ) {
755            let input = self
756                .local
757                .git
758                .save_approval_input(&self.owner, approvals)
759                .unwrap();
760            let WorktreePreparation::Prepared(plan) = self
761                .local
762                .git
763                .prepare_worktree_sync(
764                    &self.owner,
765                    &original,
766                    observation,
767                    digest,
768                    input,
769                    &AtomicBool::new(false),
770                )
771                .expect("durable save chain must prepare")
772            else {
773                panic!("unexpected duplicate")
774            };
775            let additional = self
776                .local
777                .git
778                .save_approval_input(&self.owner, [])
779                .expect("adopted approvals must release only the staging slot");
780            let receipt = self
781                .local
782                .git
783                .execute_worktree_sync(
784                    &self.owner,
785                    original.clone(),
786                    plan,
787                    digest,
788                    additional,
789                    &AtomicBool::new(false),
790                )
791                .unwrap();
792            assert!(
793                matches!(receipt.state, OperationState::Confirmed(_)),
794                "{receipt:?}"
795            );
796            assert_eq!(
797                self.local.git.existing_receipt_sync(&original).unwrap(),
798                Some(receipt)
799            );
800        }
801    }
802    #[test]
803    fn original_pending_save_chain_can_prepare_and_execute() {
804        let f = Fixture::new();
805        let (original, observation, digest) = f.capture(vec![f.file.clone()]);
806        let approval = f.save(f.file.clone(), 20, b"saved");
807        f.execute(original, observation, digest, vec![approval]);
808    }
809    #[test]
810    fn already_reflected_save_is_not_applied_twice() {
811        let f = Fixture::new();
812        let first = f.save(f.file.clone(), 21, b"first");
813        let (original, observation, digest) = f.capture(vec![f.file.clone()]);
814        let second = f.save(f.file.clone(), 22, b"second");
815        f.execute(original, observation, digest, vec![first, second]);
816    }
817    #[test]
818    fn captured_absence_allows_only_the_actual_owned_creation() {
819        let f = Fixture::new();
820        let absent = FileRef {
821            path: target_path(&f.dir.path().join("repo/new")),
822            ..f.file.clone()
823        };
824        let (original, observation, digest) = f.capture(vec![absent.clone()]);
825        let approval = f.save(absent, 23, b"created");
826        f.execute(original, observation, digest, vec![approval]);
827    }
828    #[test]
829    fn captured_creation_then_later_save_forms_one_contiguous_chain() {
830        let f = Fixture::new();
831        let absent = FileRef {
832            path: target_path(&f.dir.path().join("repo/new")),
833            ..f.file.clone()
834        };
835        let (original, observation, digest) = f.capture(vec![absent.clone()]);
836        let first = f.save(absent.clone(), 24, b"created");
837        let second = f.save(absent, 25, b"updated");
838        f.execute(original, observation, digest, vec![first, second]);
839    }
840    #[test]
841    fn interleaved_cross_window_saves_through_outside_aliases_are_contiguous() {
842        let f = Fixture::new();
843        let root = f.dir.path().join("repo");
844        std::fs::write(root.join("second"), "second").unwrap();
845        git(&root, &["add", "second"]);
846        git(&root, &["commit", "-m", "second"]);
847        git(&root, &["branch", "-f", "other"]);
848        let second = FileRef {
849            path: target_path(&root.join("second")),
850            ..f.file.clone()
851        };
852        let alias_path = f.dir.path().join("outside");
853        std::os::unix::fs::symlink(root.join("file"), &alias_path).unwrap();
854        let alias = FileRef {
855            path: target_path(&alias_path),
856            ..f.file.clone()
857        };
858        let lease = WorkspaceLeaseID([3; 16]);
859        f.local.register(WorkspaceID([4; 16]), lease).unwrap();
860        let (original, observation, digest) =
861            f.capture(vec![f.file.clone(), alias.clone(), second.clone()]);
862        let first = f.save_as(lease, alias, 30, b"alias save");
863        let other = f.save(second, 31, b"second save");
864        let last = f.save(f.file.clone(), 32, b"last save");
865        f.local.release(lease);
866        f.execute(original, observation, digest, vec![first, other, last]);
867    }
868    #[test]
869    fn external_changes_never_become_approved_save_history() {
870        use std::os::unix::fs::PermissionsExt;
871        for case in [
872            "same-content",
873            "mode",
874            "head",
875            "index",
876            "new-path",
877            "missing-chain",
878            "duplicate",
879            "reverse",
880            "external-create",
881        ] {
882            let f = Fixture::new();
883            let root = f.dir.path().join("repo");
884            let file = if case == "external-create" {
885                FileRef {
886                    path: target_path(&root.join("new")),
887                    ..f.file.clone()
888                }
889            } else {
890                f.file.clone()
891            };
892            let (original, observation, digest) = f.capture(vec![file.clone()]);
893            if case == "external-create" {
894                std::fs::write(root.join("new"), "external").unwrap();
895            }
896            let first = f.save(file.clone(), 40, b"saved");
897            let mut approvals = vec![first.clone()];
898            match case {
899                "same-content" => {
900                    std::fs::write(root.join("replacement"), b"saved").unwrap();
901                    std::fs::rename(root.join("replacement"), root.join("file")).unwrap();
902                }
903                "mode" => std::fs::set_permissions(
904                    root.join("file"),
905                    std::fs::Permissions::from_mode(0o600),
906                )
907                .unwrap(),
908                "head" => git(&root, &["checkout", "other"]),
909                "index" => git(&root, &["add", "file"]),
910                "new-path" => std::fs::write(root.join("unapproved"), "external").unwrap(),
911                "missing-chain" => approvals = vec![f.save(file, 41, b"later")],
912                "duplicate" => approvals.push(first),
913                "reverse" => approvals.insert(0, f.save(file, 41, b"later")),
914                _ => {}
915            }
916            let input = f
917                .local
918                .git
919                .save_approval_input(&f.owner, approvals)
920                .unwrap();
921            assert!(
922                f.local
923                    .git
924                    .prepare_worktree_sync(
925                        &f.owner,
926                        &original,
927                        observation,
928                        digest,
929                        input,
930                        &AtomicBool::new(false)
931                    )
932                    .is_err(),
933                "{case}"
934            );
935            assert!(
936                f.local
937                    .git
938                    .existing_receipt_sync(&original)
939                    .unwrap()
940                    .is_none(),
941                "{case}"
942            );
943        }
944    }
945    #[test]
946    fn missing_expired_malformed_and_reconciled_records_cannot_prove_saves() {
947        for case in [
948            "missing", "expired", "legacy", "unknown", "no-after", "digest", "actor", "runtime",
949            "kind", "revision", "result", "before",
950        ] {
951            let f = Fixture::new();
952            let (original, observation, digest) = f.capture(vec![f.file.clone()]);
953            let mut approval = f.save(f.file.clone(), 45, b"saved");
954            {
955                let ledger = f.local.files.ledger.lock().unwrap();
956                let _lock = ledger.lock().unwrap();
957                let mut record = ledger.read(approval.operation).unwrap().unwrap();
958                match case {
959                    "missing" => {
960                        std::fs::remove_file(
961                            ledger.directory.join(format!("{}.json", "2d".repeat(16))),
962                        )
963                        .unwrap();
964                    }
965                    "expired" => record.created = 0,
966                    "legacy" => record.save_proof = None,
967                    "unknown" => {
968                        record.receipt = crate::operations::ledger::unknown(
969                            record.envelope.id,
970                            record.envelope.target.clone(),
971                            "fixture uncertain",
972                        )
973                    }
974                    "no-after" => record.save_proof.as_mut().unwrap().after = None,
975                    "digest" => approval.request_digest[0] ^= 1,
976                    "actor" => approval.actor.lease = WorkspaceLeaseID([9; 16]),
977                    "runtime" => {
978                        record
979                            .save_proof
980                            .as_mut()
981                            .unwrap()
982                            .actor
983                            .runtime
984                            .supervisor_boot[0] ^= 1
985                    }
986                    "kind" => record.envelope.kind = MutationKind::Git,
987                    "revision" => record.revision = BufferRevision(99),
988                    "result" => {
989                        let OperationState::Confirmed(MutationResult::Saved(saved)) =
990                            &mut record.receipt.state
991                        else {
992                            panic!()
993                        };
994                        saved.version.opaque_token[0] ^= 1;
995                    }
996                    "before" => {
997                        record
998                            .save_proof
999                            .as_mut()
1000                            .unwrap()
1001                            .before
1002                            .as_mut()
1003                            .unwrap()
1004                            .canonical
1005                            .opaque_token[0] ^= 1
1006                    }
1007                    _ => unreachable!(),
1008                }
1009                if case != "missing" {
1010                    ledger.write(&record).unwrap();
1011                }
1012            }
1013            let input = f
1014                .local
1015                .git
1016                .save_approval_input(&f.owner, [approval])
1017                .unwrap();
1018            assert!(
1019                f.local
1020                    .git
1021                    .prepare_worktree_sync(
1022                        &f.owner,
1023                        &original,
1024                        observation,
1025                        digest,
1026                        input,
1027                        &AtomicBool::new(false)
1028                    )
1029                    .is_err(),
1030                "{case}"
1031            );
1032            assert!(
1033                f.local
1034                    .git
1035                    .existing_receipt_sync(&original)
1036                    .unwrap()
1037                    .is_none()
1038            );
1039        }
1040    }
1041    #[test]
1042    fn exact_registration_survives_durable_admission_or_refuses_without_effect() {
1043        for phase in ["before", "during", "after"] {
1044            let f = Fixture::new();
1045            let (original, observation, digest) = f.capture(vec![f.file.clone()]);
1046            let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1047            let WorktreePreparation::Prepared(plan) = f
1048                .local
1049                .git
1050                .prepare_worktree_sync(
1051                    &f.owner,
1052                    &original,
1053                    observation,
1054                    digest,
1055                    input,
1056                    &AtomicBool::new(false),
1057                )
1058                .unwrap()
1059            else {
1060                panic!()
1061            };
1062            let mut service = LocalGitService::new(f.local.files.clone());
1063            let local = f.local.clone();
1064            let lease = f.owner.registration().lease;
1065            let hook: Arc<dyn Fn() + Send + Sync> = Arc::new(move || local.release(lease));
1066            match phase {
1067                "before" => service.before_accept = Some(hook),
1068                "during" => service.after_accept = Some(hook),
1069                _ => service.after_authorization = Some(hook),
1070            }
1071            let input = service.save_approval_input(&f.owner, []).unwrap();
1072            let result = service.execute_worktree_sync(
1073                &f.owner,
1074                original.clone(),
1075                plan,
1076                digest,
1077                input,
1078                &AtomicBool::new(false),
1079            );
1080            if phase == "before" {
1081                assert!(result.is_err());
1082                assert!(service.existing_receipt_sync(&original).unwrap().is_none());
1083            } else {
1084                let receipt = result.unwrap();
1085                assert!(
1086                    if phase == "after" {
1087                        matches!(receipt.state, OperationState::Confirmed(_))
1088                    } else {
1089                        matches!(receipt.state, OperationState::Failed(_))
1090                    },
1091                    "{phase}: {receipt:?}"
1092                );
1093                assert_eq!(
1094                    service.existing_receipt_sync(&original).unwrap(),
1095                    Some(receipt)
1096                );
1097            }
1098        }
1099    }
1100    #[test]
1101    fn historical_canonical_identity_must_agree_with_the_same_saved_descriptor() {
1102        let f = Fixture::new();
1103        let (original, observation, digest) = f.capture(vec![f.file.clone()]);
1104        let approval = f.save(f.file.clone(), 46, b"saved");
1105        {
1106            let ledger = f.local.files.ledger.lock().unwrap();
1107            let _lock = ledger.lock().unwrap();
1108            let mut record = ledger.read(approval.operation).unwrap().unwrap();
1109            record
1110                .save_proof
1111                .as_mut()
1112                .unwrap()
1113                .after
1114                .as_mut()
1115                .unwrap()
1116                .canonical
1117                .file_identity[0] ^= 1;
1118            ledger.write(&record).unwrap();
1119        }
1120        let input = f
1121            .local
1122            .git
1123            .save_approval_input(&f.owner, [approval])
1124            .unwrap();
1125        assert!(
1126            f.local
1127                .git
1128                .prepare_worktree_sync(
1129                    &f.owner,
1130                    &original,
1131                    observation,
1132                    digest,
1133                    input,
1134                    &AtomicBool::new(false)
1135                )
1136                .is_err(),
1137            "malformed canonical endpoint was accepted"
1138        );
1139    }
1140    #[test]
1141    fn duplicate_receipt_precedes_invalid_plan_owner_cancellation_and_store() {
1142        let f = Fixture::new();
1143        let (original, _, _) = f.capture(vec![]);
1144        let first_input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1145        let other_lease = WorkspaceLeaseID([6; 16]);
1146        f.local.register(WorkspaceID([7; 16]), other_lease).unwrap();
1147        let other = f.local.observation_owner(other_lease).unwrap();
1148        let second_input = f.local.git.save_approval_input(&other, []).unwrap();
1149        let receipt = f
1150            .local
1151            .git
1152            .mutate_sync(original.clone(), &AtomicBool::new(false));
1153        assert!(matches!(receipt.state, OperationState::Confirmed(_)));
1154        f.local.release(f.owner.registration().lease);
1155        let mut service = LocalGitService::new(f.local.files.clone());
1156        service.observations = Err(io_error("fixture storage unavailable"));
1157        let WorktreePreparation::Existing(found) = service
1158            .prepare_worktree_sync(
1159                &f.owner,
1160                &original,
1161                [0; 16],
1162                [0; 32],
1163                first_input,
1164                &AtomicBool::new(true),
1165            )
1166            .unwrap()
1167        else {
1168            panic!()
1169        };
1170        assert_eq!(*found, receipt);
1171        assert_eq!(
1172            service
1173                .execute_worktree_sync(
1174                    &f.owner,
1175                    original,
1176                    [0; 16],
1177                    [0; 32],
1178                    second_input,
1179                    &AtomicBool::new(true)
1180                )
1181                .unwrap(),
1182            receipt
1183        );
1184    }
1185    #[test]
1186    fn execute_accepts_later_approvals_but_keeps_the_original_request() {
1187        let f = Fixture::new();
1188        let (original, observation, digest) = f.capture(vec![f.file.clone()]);
1189        let first = f.save(f.file.clone(), 47, b"first");
1190        let input = f.local.git.save_approval_input(&f.owner, [first]).unwrap();
1191        let WorktreePreparation::Prepared(plan) = f
1192            .local
1193            .git
1194            .prepare_worktree_sync(
1195                &f.owner,
1196                &original,
1197                observation,
1198                digest,
1199                input,
1200                &AtomicBool::new(false),
1201            )
1202            .unwrap()
1203        else {
1204            panic!()
1205        };
1206        let second = f.save(f.file.clone(), 48, b"second");
1207        let input = f.local.git.save_approval_input(&f.owner, [second]).unwrap();
1208        let receipt = f
1209            .local
1210            .git
1211            .execute_worktree_sync(
1212                &f.owner,
1213                original.clone(),
1214                plan,
1215                digest,
1216                input,
1217                &AtomicBool::new(false),
1218            )
1219            .unwrap();
1220        assert!(matches!(receipt.state, OperationState::Confirmed(_)));
1221        let ledger = f.local.files.ledger.lock().unwrap();
1222        let record = ledger.read(original.operation).unwrap().unwrap();
1223        assert_eq!(
1224            record.envelope.request_digest,
1225            request_digest(&original).unwrap()
1226        );
1227    }
1228    #[test]
1229    fn prepared_sibling_worktree_can_save_and_prepare_independently() {
1230        let f = Fixture::new();
1231        let root = f.dir.path().join("repo");
1232        let linked = f.dir.path().join("linked");
1233        git(
1234            &root,
1235            &["worktree", "add", "-b", "linked", linked.to_str().unwrap()],
1236        );
1237        let sibling_file = FileRef {
1238            path: target_path(&linked.join("file")),
1239            ..f.file.clone()
1240        };
1241        let lease = WorkspaceLeaseID([6; 16]);
1242        f.local.register(WorkspaceID([7; 16]), lease).unwrap();
1243        let owner = f.local.observation_owner(lease).unwrap();
1244        let sibling_worktree = f
1245            .local
1246            .git
1247            .discover_sync(
1248                FileRef {
1249                    path: target_path(&linked),
1250                    ..f.file.clone()
1251                },
1252                &AtomicBool::new(false),
1253            )
1254            .unwrap()
1255            .unwrap();
1256        let (original, observation, digest) = f.capture(vec![f.file.clone(), sibling_file.clone()]);
1257        let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1258        let WorktreePreparation::Prepared(plan) = f
1259            .local
1260            .git
1261            .prepare_worktree_sync(
1262                &f.owner,
1263                &original,
1264                observation,
1265                digest,
1266                input,
1267                &AtomicBool::new(false),
1268            )
1269            .unwrap()
1270        else {
1271            panic!()
1272        };
1273        let sibling = f
1274            .local
1275            .git
1276            .observe_owned_sync(
1277                &owner,
1278                sibling_worktree.clone(),
1279                [Ok(sibling_file.clone())],
1280                &AtomicBool::new(false),
1281            )
1282            .unwrap();
1283        let sibling_digest = f
1284            .local
1285            .git
1286            .observation_alias_digest(&owner, sibling.id)
1287            .unwrap();
1288        let sibling_original = GitMutationRequest {
1289            operation: OperationID([91; 16]),
1290            worktree: sibling_worktree,
1291            expected: sibling.snapshot.state,
1292            command: GitMutation::Checkout {
1293                reference: b"linked".to_vec(),
1294            },
1295        };
1296        let input = f.local.git.save_approval_input(&owner, []).unwrap();
1297        let WorktreePreparation::Prepared(sibling_plan) = f
1298            .local
1299            .git
1300            .prepare_worktree_sync(
1301                &owner,
1302                &sibling_original,
1303                sibling.id,
1304                sibling_digest,
1305                input,
1306                &AtomicBool::new(false),
1307            )
1308            .unwrap()
1309        else {
1310            panic!()
1311        };
1312        f.save_as(lease, sibling_file, 49, b"sibling remains editable");
1313        let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1314        assert!(matches!(
1315            f.local
1316                .git
1317                .execute_worktree_sync(
1318                    &f.owner,
1319                    original,
1320                    plan,
1321                    digest,
1322                    input,
1323                    &AtomicBool::new(false)
1324                )
1325                .unwrap()
1326                .state,
1327            OperationState::Confirmed(_)
1328        ));
1329        f.local.git.cancel_worktree_plan(&owner, sibling_plan);
1330    }
1331    #[test]
1332    fn ignored_alias_is_rechecked_after_durable_acceptance() {
1333        let f = Fixture::new();
1334        let root = f.dir.path().join("repo");
1335        std::fs::write(root.join(".git/info/exclude"), "ignored\n").unwrap();
1336        std::fs::write(root.join("ignored"), "original").unwrap();
1337        let file = FileRef {
1338            path: target_path(&root.join("ignored")),
1339            ..f.file.clone()
1340        };
1341        let (original, observation, digest) = f.capture(vec![file]);
1342        let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1343        let WorktreePreparation::Prepared(plan) = f
1344            .local
1345            .git
1346            .prepare_worktree_sync(
1347                &f.owner,
1348                &original,
1349                observation,
1350                digest,
1351                input,
1352                &AtomicBool::new(false),
1353            )
1354            .unwrap()
1355        else {
1356            panic!()
1357        };
1358        let mut service = LocalGitService::new(f.local.files.clone());
1359        service.after_authorization = Some(Arc::new(move || {
1360            std::fs::write(root.join("ignored"), "external").unwrap()
1361        }));
1362        let input = service.save_approval_input(&f.owner, []).unwrap();
1363        let receipt = service
1364            .execute_worktree_sync(
1365                &f.owner,
1366                original.clone(),
1367                plan,
1368                digest,
1369                input,
1370                &AtomicBool::new(false),
1371            )
1372            .unwrap();
1373        assert!(matches!(
1374            receipt.state,
1375            OperationState::Failed(OperationFailure {
1376                effect: FailureEffect::ProvenNoEffect,
1377                ..
1378            })
1379        ));
1380        assert_eq!(
1381            service.existing_receipt_sync(&original).unwrap(),
1382            Some(receipt)
1383        );
1384    }
1385    #[test]
1386    fn concurrent_original_acceptance_wins_over_stale_cancelled_proof_after_preflight() {
1387        for preparing in [false, true] {
1388            let f = Fixture::new();
1389            let (original, _, _) = f.capture(vec![]);
1390            let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1391            let entered = Arc::new(std::sync::Barrier::new(2));
1392            let resume = Arc::new(std::sync::Barrier::new(2));
1393            let mut service = LocalGitService::new(f.local.files.clone());
1394            let (at_lookup, continue_lookup) = (entered.clone(), resume.clone());
1395            service.after_proof_lookup = Some(Arc::new(move || {
1396                at_lookup.wait();
1397                continue_lookup.wait();
1398            }));
1399            let owner = f.owner.clone();
1400            let duplicate = original.clone();
1401            let worker = std::thread::spawn(move || {
1402                if preparing {
1403                    let WorktreePreparation::Existing(receipt) = service
1404                        .prepare_worktree_sync(
1405                            &owner,
1406                            &duplicate,
1407                            [0; 16],
1408                            [0; 32],
1409                            input,
1410                            &AtomicBool::new(true),
1411                        )
1412                        .unwrap()
1413                    else {
1414                        panic!()
1415                    };
1416                    *receipt
1417                } else {
1418                    service
1419                        .execute_worktree_sync(
1420                            &owner,
1421                            duplicate,
1422                            [0; 16],
1423                            [0; 32],
1424                            input,
1425                            &AtomicBool::new(true),
1426                        )
1427                        .unwrap()
1428                }
1429            });
1430            entered.wait();
1431            let expected = f
1432                .local
1433                .git
1434                .mutate_sync(original.clone(), &AtomicBool::new(false));
1435            resume.wait();
1436            assert!(matches!(expected.state, OperationState::Confirmed(_)));
1437            assert_eq!(worker.join().unwrap(), expected);
1438            assert_eq!(
1439                f.local.git.existing_receipt_sync(&original).unwrap(),
1440                Some(expected)
1441            );
1442        }
1443    }
1444    #[test]
1445    fn legacy_lookup_never_waits_for_repository_execution_and_existing_receipt_wins() {
1446        let f = Fixture::new();
1447        let (original, _, _) = f.capture(vec![]);
1448        let mutex = super::super::mutations::repository_mutex(&original.worktree);
1449        let held = mutex.lock().unwrap();
1450        assert_eq!(
1451            f.local
1452                .git
1453                .refuse_unprepared_worktree_sync(&original)
1454                .unwrap_err()
1455                .code,
1456            ErrorCode::Unavailable
1457        );
1458        drop(held);
1459        let expected = f
1460            .local
1461            .git
1462            .mutate_sync(original.clone(), &AtomicBool::new(false));
1463        assert!(matches!(expected.state, OperationState::Confirmed(_)));
1464        let _held = mutex.lock().unwrap();
1465        assert_eq!(
1466            f.local
1467                .git
1468                .refuse_unprepared_worktree_sync(&original)
1469                .unwrap(),
1470            expected
1471        );
1472    }
1473    #[test]
1474    fn legacy_refusal_rechecks_original_accepted_after_fast_preflight() {
1475        let f = Fixture::new();
1476        let (original, _, _) = f.capture(vec![]);
1477        let local = f.local.clone();
1478        let concurrent = original.clone();
1479        let mut service = LocalGitService::new(f.local.files.clone());
1480        service.after_proof_lookup = Some(Arc::new(move || {
1481            assert!(matches!(
1482                local
1483                    .git
1484                    .mutate_sync(concurrent.clone(), &AtomicBool::new(false))
1485                    .state,
1486                OperationState::Confirmed(_)
1487            ));
1488        }));
1489        let receipt = service.refuse_unprepared_worktree_sync(&original).unwrap();
1490        assert_eq!(
1491            Some(receipt),
1492            f.local.git.existing_receipt_sync(&original).unwrap()
1493        );
1494    }
1495    #[test]
1496    fn legacy_existing_accepted_lookup_does_not_wait_for_the_executing_repository() {
1497        let f = Fixture::new();
1498        let (original, _, _) = f.capture(vec![]);
1499        let local = f.local.clone();
1500        let duplicate = original.clone();
1501        let mut service = LocalGitService::new(f.local.files.clone());
1502        service.after_authorization = Some(Arc::new(move || {
1503            let receipt = local
1504                .git
1505                .refuse_unprepared_worktree_sync(&duplicate)
1506                .unwrap();
1507            assert!(matches!(receipt.state, OperationState::Unknown(_)));
1508        }));
1509        let receipt = service.mutate_sync(original.clone(), &AtomicBool::new(false));
1510        assert!(matches!(receipt.state, OperationState::Confirmed(_)));
1511        assert_eq!(
1512            Some(receipt),
1513            f.local.git.existing_receipt_sync(&original).unwrap()
1514        );
1515    }
1516    #[test]
1517    fn hintless_owned_save_uses_captured_same_path_without_rewriting_alias_identity() {
1518        for changed_hint in [false, true] {
1519            let f = Fixture::new();
1520            let outside = f.dir.path().join("original-alias");
1521            std::os::unix::fs::symlink(f.dir.path().join("repo/file"), &outside).unwrap();
1522            let captured = FileRef {
1523                path: target_path(&outside),
1524                resolved_identity: Some(vec![7]),
1525                ..f.file.clone()
1526            };
1527            let (original, observation, digest) = f.capture(vec![captured.clone()]);
1528            let effective = FileRef {
1529                resolved_identity: if changed_hint { Some(vec![8]) } else { None },
1530                ..captured.clone()
1531            };
1532            let approval = f.save(effective, 48, b"owned save");
1533            let input = f
1534                .local
1535                .git
1536                .save_approval_input(&f.owner, [approval])
1537                .unwrap();
1538            let result = f.local.git.prepare_worktree_sync(
1539                &f.owner,
1540                &original,
1541                observation,
1542                digest,
1543                input,
1544                &AtomicBool::new(false),
1545            );
1546            if changed_hint {
1547                assert!(result.is_err());
1548            } else {
1549                let WorktreePreparation::Prepared(plan) = result.unwrap() else {
1550                    panic!("expected prepared")
1551                };
1552                assert_eq!(
1553                    f.local
1554                        .git
1555                        .observation_alias_digest(&f.owner, observation)
1556                        .unwrap(),
1557                    digest
1558                );
1559                let mut reader = f
1560                    .local
1561                    .git
1562                    .observation_reader(&f.owner, observation)
1563                    .unwrap();
1564                let mut found = false;
1565                while let Some(record) = reader.next_record().unwrap() {
1566                    if record.membership.is_some() {
1567                        assert!(record.matches_alias(&captured).unwrap());
1568                        found = true;
1569                    }
1570                }
1571                assert!(found);
1572                drop(reader);
1573                f.local.git.cancel_worktree_plan(&f.owner, plan);
1574            }
1575        }
1576    }
1577    #[test]
1578    fn original_alias_set_and_link_identity_cannot_be_reconstructed_later() {
1579        for case in [
1580            "uncovered",
1581            "same-target-link-replaced",
1582            "hint-changed",
1583            "membership-changed",
1584        ] {
1585            let f = Fixture::new();
1586            let root = f.dir.path().join("repo");
1587            let outside = f.dir.path().join("alias");
1588            let destination = if case == "membership-changed" {
1589                let path = f.dir.path().join("unrelated");
1590                std::fs::write(&path, "unrelated").unwrap();
1591                path
1592            } else {
1593                root.join("file")
1594            };
1595            std::os::unix::fs::symlink(&destination, &outside).unwrap();
1596            let mut alias = FileRef {
1597                path: target_path(&outside),
1598                ..f.file.clone()
1599            };
1600            let aliases = if case == "uncovered" {
1601                vec![f.file.clone()]
1602            } else {
1603                vec![f.file.clone(), alias.clone()]
1604            };
1605            let (original, observation, digest) = f.capture(aliases);
1606            let approvals = if case == "membership-changed" {
1607                vec![]
1608            } else {
1609                if case == "hint-changed" {
1610                    alias.resolved_identity = Some(vec![7]);
1611                }
1612                vec![f.save(alias, 50, b"saved through alias")]
1613            };
1614            if matches!(case, "same-target-link-replaced" | "membership-changed") {
1615                let replacement = f.dir.path().join("new-link");
1616                std::os::unix::fs::symlink(root.join("file"), &replacement).unwrap();
1617                std::fs::rename(replacement, outside).unwrap();
1618            }
1619            let input = f
1620                .local
1621                .git
1622                .save_approval_input(&f.owner, approvals)
1623                .unwrap();
1624            assert!(
1625                f.local
1626                    .git
1627                    .prepare_worktree_sync(
1628                        &f.owner,
1629                        &original,
1630                        observation,
1631                        digest,
1632                        input,
1633                        &AtomicBool::new(false)
1634                    )
1635                    .is_err(),
1636                "{case}"
1637            );
1638        }
1639    }
1640    #[test]
1641    fn owned_creation_through_original_directory_alias_is_verified() {
1642        let f = Fixture::new();
1643        let root = f.dir.path().join("repo");
1644        let alias = f.dir.path().join("alias-dir");
1645        std::os::unix::fs::symlink(&root, &alias).unwrap();
1646        let file = FileRef {
1647            path: target_path(&alias.join("new")),
1648            ..f.file.clone()
1649        };
1650        let (original, observation, digest) = f.capture(vec![file.clone()]);
1651        let approval = f.save(file, 51, b"created through original parent link");
1652        f.execute(original, observation, digest, vec![approval]);
1653    }
1654    #[test]
1655    fn nested_repository_and_submodule_aliases_preserve_original_membership() {
1656        let f = Fixture::new();
1657        let root = f.dir.path().join("repo");
1658        let nested = root.join("nested");
1659        std::fs::create_dir(&nested).unwrap();
1660        git(&nested, &["init", "-b", "nested"]);
1661        std::fs::write(nested.join("file"), "nested").unwrap();
1662        std::fs::write(root.join(".git/info/exclude"), "nested/\n").unwrap();
1663        let source = f.dir.path().join("source");
1664        std::fs::create_dir(&source).unwrap();
1665        git(&source, &["init", "-b", "main"]);
1666        git(&source, &["config", "user.name", "Fixture"]);
1667        git(
1668            &source,
1669            &["config", "user.email", "fixture@example.invalid"],
1670        );
1671        std::fs::write(source.join("file"), "submodule").unwrap();
1672        git(&source, &["add", "file"]);
1673        git(&source, &["commit", "-m", "initial"]);
1674        git(
1675            &root,
1676            &[
1677                "-c",
1678                "protocol.file.allow=always",
1679                "submodule",
1680                "add",
1681                source.to_str().unwrap(),
1682                "sub",
1683            ],
1684        );
1685        git(&root, &["commit", "-am", "submodule"]);
1686        git(&root, &["branch", "-f", "other"]);
1687        let files = [nested.join("file"), root.join("sub/file")].map(|path| FileRef {
1688            path: target_path(&path),
1689            ..f.file.clone()
1690        });
1691        let (original, observation, digest) = f.capture(files.to_vec());
1692        let mut reader = f
1693            .local
1694            .git
1695            .observation_reader(&f.owner, observation)
1696            .unwrap();
1697        let mut unaffected = 0;
1698        while let Some(record) = reader.next_record().unwrap() {
1699            if let Some(membership) = record.membership {
1700                assert!(!membership.affected);
1701                assert!(membership.requested_repository.is_some());
1702                unaffected += 1;
1703            }
1704        }
1705        assert_eq!(unaffected, 2);
1706        drop(reader);
1707        f.execute(original, observation, digest, vec![]);
1708    }
1709    #[test]
1710    fn truncated_approval_input_cannot_publish_a_plan_or_leak_its_pin() {
1711        let f = Fixture::new();
1712        let (original, observation, digest) = f.capture(vec![]);
1713        let before = f.local.git.observation_storage_stats().unwrap().bytes;
1714        let mut input = f.local.git.observation_input(&f.owner).unwrap();
1715        input.append(&[0; 95]).unwrap();
1716        assert!(
1717            f.local
1718                .git
1719                .prepare_worktree_sync(
1720                    &f.owner,
1721                    &original,
1722                    observation,
1723                    digest,
1724                    input.seal().unwrap(),
1725                    &AtomicBool::new(false)
1726                )
1727                .is_err()
1728        );
1729        let stats = f.local.git.observation_storage_stats().unwrap();
1730        assert_eq!(stats.bytes, before);
1731        assert_eq!(stats.pins, 0);
1732        assert_eq!(stats.captures, 0);
1733    }
1734    #[test]
1735    fn plan_cancel_or_observation_release_must_survive_durable_admission() {
1736        for retirement in ["cancel", "release", "expire"] {
1737            for phase in ["before", "during", "after"] {
1738                let f = Fixture::new();
1739                let (original, observation, digest) = f.capture(vec![]);
1740                let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1741                let WorktreePreparation::Prepared(plan) = f
1742                    .local
1743                    .git
1744                    .prepare_worktree_sync(
1745                        &f.owner,
1746                        &original,
1747                        observation,
1748                        digest,
1749                        input,
1750                        &AtomicBool::new(false),
1751                    )
1752                    .unwrap()
1753                else {
1754                    panic!()
1755                };
1756                let mut service = LocalGitService::new(f.local.files.clone());
1757                let local = f.local.clone();
1758                let owner = f.owner.clone();
1759                let hook: Arc<dyn Fn() + Send + Sync> = Arc::new(move || match retirement {
1760                    "release" => local.git.release_observation(&owner, observation),
1761                    "expire" => super::super::observation_store::testing::expire_owner(
1762                        local.git.observation_store().unwrap(),
1763                        &owner,
1764                    ),
1765                    _ => local.git.cancel_worktree_plan(&owner, plan),
1766                });
1767                match phase {
1768                    "before" => service.before_accept = Some(hook),
1769                    "during" => service.after_accept = Some(hook),
1770                    _ => service.after_authorization = Some(hook),
1771                }
1772                let input = service.save_approval_input(&f.owner, []).unwrap();
1773                let result = service.execute_worktree_sync(
1774                    &f.owner,
1775                    original.clone(),
1776                    plan,
1777                    digest,
1778                    input,
1779                    &AtomicBool::new(false),
1780                );
1781                if phase == "before" {
1782                    assert!(
1783                        result.is_err(),
1784                        "cancelled/released plan accepted before fsync"
1785                    );
1786                    assert!(service.existing_receipt_sync(&original).unwrap().is_none());
1787                } else {
1788                    let receipt = result.unwrap();
1789                    assert!(
1790                        if phase == "after" {
1791                            matches!(receipt.state, OperationState::Confirmed(_))
1792                        } else {
1793                            matches!(
1794                                receipt.state,
1795                                OperationState::Failed(OperationFailure {
1796                                    effect: FailureEffect::ProvenNoEffect,
1797                                    ..
1798                                })
1799                            )
1800                        },
1801                        "{phase}, {retirement}: {receipt:?}"
1802                    );
1803                }
1804            }
1805        }
1806    }
1807    #[test]
1808    fn unrecordable_revocation_remains_unknown_and_the_accepted_id_never_replays() {
1809        let f = Fixture::new();
1810        let (original, observation, digest) = f.capture(vec![]);
1811        let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1812        let WorktreePreparation::Prepared(plan) = f
1813            .local
1814            .git
1815            .prepare_worktree_sync(
1816                &f.owner,
1817                &original,
1818                observation,
1819                digest,
1820                input,
1821                &AtomicBool::new(false),
1822            )
1823            .unwrap()
1824        else {
1825            panic!()
1826        };
1827        let mut service = LocalGitService::new(f.local.files.clone());
1828        let directory = f.local.files.ledger.lock().unwrap().directory.clone();
1829        let relocated = directory.with_file_name("temporarily-unavailable-operations");
1830        let (original_directory, unavailable_directory) = (directory.clone(), relocated.clone());
1831        let local = f.local.clone();
1832        let lease = f.owner.registration().lease;
1833        service.after_accept = Some(Arc::new(move || {
1834            local.release(lease);
1835            std::fs::rename(&original_directory, &unavailable_directory).unwrap();
1836        }));
1837        let input = service.save_approval_input(&f.owner, []).unwrap();
1838        let receipt = service
1839            .execute_worktree_sync(
1840                &f.owner,
1841                original.clone(),
1842                plan,
1843                digest,
1844                input,
1845                &AtomicBool::new(false),
1846            )
1847            .unwrap();
1848        assert!(matches!(receipt.state, OperationState::Unknown(_)));
1849        std::fs::rename(relocated, directory).unwrap();
1850        assert!(matches!(
1851            service
1852                .mutate_sync(original.clone(), &AtomicBool::new(false))
1853                .state,
1854            OperationState::Unknown(_)
1855        ));
1856        assert_eq!(
1857            service
1858                .status_sync(original.worktree, &AtomicBool::new(false))
1859                .unwrap()
1860                .state,
1861            original.expected
1862        );
1863    }
1864
1865    #[test]
1866    fn immutable_original_request_bytes_are_retained_and_charged_with_the_plan() {
1867        let f = Fixture::new();
1868        let (original, observation, digest) = f.capture(vec![]);
1869        let baseline = f.local.git.observation_storage_stats().unwrap().bytes;
1870        let input = f.local.git.save_approval_input(&f.owner, []).unwrap();
1871        let WorktreePreparation::Prepared(plan) = f
1872            .local
1873            .git
1874            .prepare_worktree_sync(
1875                &f.owner,
1876                &original,
1877                observation,
1878                digest,
1879                input,
1880                &AtomicBool::new(false),
1881            )
1882            .unwrap()
1883        else {
1884            panic!()
1885        };
1886        assert_eq!(
1887            f.local.git.observation_storage_stats().unwrap().bytes,
1888            baseline + serde_json::to_vec(&original).unwrap().len() as u64,
1889            "full immutable request is missing from charged plan storage"
1890        );
1891        let store = f.local.git.observation_store().unwrap();
1892        let retained = store.plan(&f.owner, plan).unwrap();
1893        assert_eq!(retained.request_digest, request_digest(&original).unwrap());
1894        let mut bytes = Vec::new();
1895        cursor(&retained._original_request)
1896            .read_to_end(&mut bytes)
1897            .unwrap();
1898        assert_eq!(bytes, serde_json::to_vec(&original).unwrap());
1899        drop(retained);
1900        f.local.git.cancel_worktree_plan(&f.owner, plan);
1901        assert_eq!(
1902            f.local.git.observation_storage_stats().unwrap().bytes,
1903            baseline
1904        );
1905    }
1906    #[test]
1907    #[ignore = "explicit modest verifier geometry measurement"]
1908    fn actual_verifier_chain_and_alias_measurement() {
1909        fn rss() -> i64 {
1910            let mut usage = std::mem::MaybeUninit::uninit();
1911            assert_eq!(
1912                unsafe { libc::getrusage(libc::RUSAGE_SELF, usage.as_mut_ptr()) },
1913                0
1914            );
1915            unsafe { usage.assume_init() }.ru_maxrss
1916        }
1917        let f = Fixture::new();
1918        let root = f.dir.path().join("repo");
1919        let mut files = vec![];
1920        for n in 0..64 {
1921            let path = root.join(format!("path-{n:026}"));
1922            std::fs::write(&path, b"original\n").unwrap();
1923            files.push(FileRef {
1924                path: target_path(&path),
1925                ..f.file.clone()
1926            });
1927        }
1928        git(&root, &["add", "."]);
1929        git(&root, &["commit", "-m", "geometry"]);
1930        git(&root, &["branch", "-f", "other"]);
1931        let mut destination = root.join("file");
1932        for n in 0..8 {
1933            let link = f.dir.path().join(format!("indirect-{n}"));
1934            std::os::unix::fs::symlink(destination, &link).unwrap();
1935            destination = link;
1936        }
1937        let mut aliases = vec![f.file.clone()];
1938        aliases.extend(files.iter().take(16).cloned());
1939        for n in 0..16 {
1940            let path = f.dir.path().join(format!("outside-{n}"));
1941            std::os::unix::fs::symlink(&destination, &path).unwrap();
1942            aliases.push(FileRef {
1943                path: target_path(&path),
1944                ..f.file.clone()
1945            });
1946        }
1947        let rss_before = rss();
1948        let capture_start = Instant::now();
1949        let (original, observation, digest) = f.capture(aliases);
1950        let capture_seconds = capture_start.elapsed().as_secs_f64();
1951        let original_bytes = f.local.git.observation_storage_stats().unwrap().bytes;
1952        let mut approvals = vec![f.save(f.file.clone(), 100, b"alias endpoint saved")];
1953        for round in 0..2 {
1954            for (n, file) in files.iter().take(16).enumerate() {
1955                approvals.push(f.save(
1956                    file.clone(),
1957                    (101 + round * 16 + n) as u8,
1958                    if round == 0 { b"first" } else { b"final" },
1959                ));
1960            }
1961        }
1962        let input = f
1963            .local
1964            .git
1965            .save_approval_input(&f.owner, approvals)
1966            .unwrap();
1967        let approval_bytes = input.len();
1968        let prepare_start = Instant::now();
1969        let WorktreePreparation::Prepared(plan_id) = f
1970            .local
1971            .git
1972            .prepare_worktree_sync(
1973                &f.owner,
1974                &original,
1975                observation,
1976                digest,
1977                input,
1978                &AtomicBool::new(false),
1979            )
1980            .unwrap()
1981        else {
1982            panic!()
1983        };
1984        let prepare_seconds = prepare_start.elapsed().as_secs_f64();
1985        let retained_plan_bytes = f.local.git.observation_storage_stats().unwrap().bytes;
1986        let store = f.local.git.observation_store().unwrap();
1987        let plan = store.plan(&f.owner, plan_id).unwrap();
1988        let evidence = f
1989            .local
1990            .git
1991            .freeze_evidence(&f.owner, &plan, None, &AtomicBool::new(false))
1992            .unwrap();
1993        let sampled_bytes = store.stats().bytes;
1994        let mut reader = cursor(&evidence);
1995        let mut max_record_bytes = 0;
1996        let mut records = 0;
1997        while let Some(record) = read_evidence(&mut reader).unwrap() {
1998            max_record_bytes = max_record_bytes.max(serde_json::to_vec(&record).unwrap().len());
1999            records += 1;
2000        }
2001        drop(reader);
2002        drop(evidence);
2003        drop(plan);
2004        let additional = f.local.git.save_approval_input(&f.owner, []).unwrap();
2005        let execute_start = Instant::now();
2006        let receipt = f
2007            .local
2008            .git
2009            .execute_worktree_sync(
2010                &f.owner,
2011                original,
2012                plan_id,
2013                digest,
2014                additional,
2015                &AtomicBool::new(false),
2016            )
2017            .unwrap();
2018        let execute_seconds = execute_start.elapsed().as_secs_f64();
2019        assert!(matches!(receipt.state, OperationState::Confirmed(_)));
2020        println!(
2021            "{}",
2022            serde_json::json!({"git_nodes":65,"supplemental_aliases":33,"outside_aliases":16,"indirect_links":8,"save_records":records,
2023            "original_bytes":original_bytes,"approval_bytes":approval_bytes,"retained_plan_bytes":retained_plan_bytes,"sampled_with_frozen_evidence_bytes":sampled_bytes,
2024            "max_encoded_evidence_record_bytes":max_record_bytes,"capture_seconds":capture_seconds,"prepare_seconds":prepare_seconds,"execute_seconds":execute_seconds,
2025            "rss_before":rss_before,"rss_after":rss(),"hash_scratch_bytes_per_worker":65536,"ledger_record_parse_bound":131072})
2026        );
2027        f.local.git.release_observation(&f.owner, observation);
2028        assert_eq!(store.stats().bytes, 0);
2029    }
2030}