Skip to main content

git_policy_cli/
commit_landing_loop.rs

1//! What:
2//!  The landing loop: land, and after each lost race replay and revalidate outside both locks, until the commit
3//!  lands, its replay conflicts, revalidation rejects it, or the target cannot take it.
4//! Why:
5//!  Each replay rebuilds the prepared commit from the preparation base onto the target that won, so every replay
6//!  starts from the same prepared change. After `landing.reserveAfterLostRaces` lost races the transaction takes the
7//!  landing reservation before it replays, and every landing attempt yields to another transaction's live
8//!  reservation; the reservation is released when the loop ends (`src/policy-engine/commit-landing-loop.ts`).
9//!
10//! In TS you'd write (pseudocode):
11//! ```ts
12//! const outcome = await landWithReplay({ context, mode, prepared, settled, options, committedPaths, ... });
13//! ```
14
15use super::capture_invocation::InvocationCapture;
16use super::commit_facts::PrivateIndexFacts;
17use super::commit_landing_lock::{acquire_reserved_lock, landing_is_nested};
18use super::commit_preparation::PreparedCommit;
19use super::commit_replay_options::ReplayOptions;
20use super::commit_revalidation::{Revalidation, RevalidationContext, revalidate_replay};
21use super::commit_transaction::{CommitInvocation, TransactionStop, failed};
22use super::commit_workspace::CommitWorkspace;
23use super::commit_worktree_records::{HookChanges, settled_entry};
24use super::diagnostic_log::debug;
25use super::event_path::EventPath;
26use super::landing_attempt::{LandingOutcome, LandingPayload, LandingRequest, land_transaction};
27use super::landing_findings::{LANDING_FINDING_EXIT_CODE, landing_finding};
28use super::landing_objects::migrate_shadow_objects;
29use super::owner_lock::OwnerLock;
30use super::policy_checks::ShippedChecks;
31use super::policy_engine::StageRequest;
32use super::policy_events::PolicyEvent;
33use super::replay_commit::{MergeStep, ReplayError, ReplayRequest, merge_replay, rebuild_replay};
34use super::replay_merge::ReplayedCommit;
35use super::replay_reservation::{LandingReservation, ReservationError};
36use super::repository_facts::RepositoryFacts;
37use super::transaction_journal::{AddedPath, Base, TransactionMode};
38use super::transaction_phase::{Phase, reach_transaction_phase};
39use std::path::{Path, PathBuf};
40
41/// Module tag for diagnostics.
42const TAG: &str = "landWithReplay";
43
44/// What:
45///  What the next landing attempt lands.
46/// Why:
47///   The candidate is replaced after each replay and lives only inside the loop.
48///
49/// In TS you'd write (pseudocode):
50/// ```ts
51/// type LandingCandidate = { newOid; treeOid; expectedOld; indexPath; exactPrivateIndex; approvedTree; committedPaths; ... };
52/// ```
53struct Candidate {
54    /// The commit to land.
55    new_oid: String,
56    /// Its tree.
57    tree: String,
58    /// The target value its parent names.
59    expected_old: Base,
60    /// The private index holding its tree.
61    index: PathBuf,
62    /// Whether that index is the one native preparation committed.
63    exact_private_index: bool,
64    /// The tree `pre-commit` last approved.
65    approved_tree: String,
66    /// The paths an explicit-path landing resets.
67    committed_paths: Vec<Vec<u8>>,
68    /// The policy-added paths.
69    added_paths: Vec<AddedPath>,
70    /// The selected, corrected and hook-changed worktree completions.
71    worktree_records: Vec<AddedPath>,
72    /// Everything commit hooks staged so far.
73    hook_changes: HookChanges,
74    /// The events of lost races, replays and revalidations so far.
75    events: Vec<PolicyEvent>,
76    /// The landing attempt number.
77    attempt: i64,
78    /// The lost races so far.
79    lost_races: u64,
80}
81
82/// What:
83///  The inputs of the loop.
84/// Why:
85///   Replay and revalidation reuse the invocation's facts.
86///
87/// In TS you'd write (pseudocode):
88/// ```ts
89/// type LandingLoopInput = { context; mode; prepared; settled; options; committedPaths; addedPaths; worktreeRecords; hookChanges; indexLockTimeoutMs; reserveAfterLostRaces };
90/// ```
91pub struct LoopInput<'a, F: RepositoryFacts> {
92    /// The invocation's fixed facts.
93    pub invocation: &'a CommitInvocation<'a>,
94    /// The published workspace.
95    pub workspace: &'a mut CommitWorkspace,
96    /// The invocation capture.
97    pub capture: &'a InvocationCapture,
98    /// The commit selection mode.
99    pub mode: TransactionMode,
100    /// The request the policies run under.
101    pub request: &'a StageRequest,
102    /// The shipped checks over the private index.
103    pub checks: &'a mut ShippedChecks<PrivateIndexFacts<F>>,
104    /// The verified prepared commit.
105    pub prepared: &'a PreparedCommit,
106    /// The settled preparation pass's events with its fix summary.
107    pub settled_events: Vec<PolicyEvent>,
108    /// The replay-relevant commit options.
109    pub options: &'a ReplayOptions,
110    /// The caller's kept global options.
111    pub global_args: &'a [String],
112    /// Whether interactive or include selection forbids automatic patches.
113    pub read_only_selection: bool,
114    /// The paths an explicit-path commit carries.
115    pub committed_paths: Vec<Vec<u8>>,
116    /// The policy-added paths.
117    pub added_paths: Vec<AddedPath>,
118    /// The worktree completions.
119    pub worktree_records: Vec<AddedPath>,
120    /// What preparation hooks staged.
121    pub hook_changes: HookChanges,
122    /// The backoff budget for a foreign `index.lock`.
123    pub index_lock_timeout_ms: u64,
124    /// The lost landing races after which the transaction takes the landing reservation.
125    pub reserve_after_lost_races: u64,
126}
127
128/// What:
129///  How the loop ended.
130/// Why:
131///   A landed commit continues to post-landing completion; a failure carries the complete events and the status.
132///
133/// In TS you'd write (pseudocode):
134/// ```ts
135/// type LandingLoopOutcome = { kind: 'landed'; oid; events; worktreeRecords; addedPaths } | { kind: 'failed'; result };
136/// ```
137pub enum LoopOutcome {
138    /// The commit landed.
139    Landed {
140        /// The landed commit.
141        oid: String,
142        /// The events to append after the settled preparation pass.
143        events: Vec<PolicyEvent>,
144        /// The worktree completions for the landed commit.
145        worktree_records: Vec<AddedPath>,
146        /// The policy-added paths of the landed commit.
147        added_paths: Vec<AddedPath>,
148    },
149    /// Nothing landed.
150    Failed {
151        /// The complete blocking events.
152        events: Vec<PolicyEvent>,
153        /// The exit status.
154        exit_code: i32,
155    },
156}
157
158/// What:
159///  Points worktree completions at the blobs a replayed tree holds, dropping paths it no longer holds as ordinary files.
160/// Why:
161///   A completion names the blob the commit now carries for its path.
162///
163/// In TS you'd write (pseudocode):
164/// ```ts
165/// await retargetRecords({ context, indexPath, records });
166/// ```
167fn retarget_records(
168    input: &CommitInvocation<'_>,
169    index: &Path,
170    records: &[AddedPath],
171) -> Result<Vec<AddedPath>, TransactionStop> {
172    let mut retargeted: Vec<AddedPath> = Vec::new();
173    for record in records {
174        let Some((mode, oid)) = settled_entry(input.context, input.cwd, index, record.path.as_slice())? else {
175            continue;
176        };
177        if mode != record.git_mode {
178            continue;
179        }
180        retargeted.push(AddedPath {
181            intended_oid: oid,
182            ..record.clone()
183        });
184    }
185    return Ok(retargeted);
186}
187
188/// What:
189///  The replay request of one lost race.
190/// Why:
191///   Every replay step reads the same facts, so they are built once.
192///
193/// In TS you'd write (pseudocode):
194/// ```ts
195/// { gitPath, shadowPath, objectDirectory, cwd, directory, replay, mergeBase, current, prepared }
196/// ```
197fn replay_request<'a, F: RepositoryFacts>(
198    input: &'a LoopInput<'a, F>,
199    replay: u64,
200    onto: &'a str,
201) -> ReplayRequest<'a> {
202    return ReplayRequest {
203        context: input.invocation.context,
204        shadow: input.workspace.landing.shadow.as_path(),
205        object_directory: input.workspace.object_directory.as_path(),
206        worktree_root: input.capture.layout.repository_root.as_path(),
207        git_dir: input.capture.layout.git_dir.as_path(),
208        transaction_directory: input.workspace.landing.directory.as_path(),
209        replay,
210        base: match &input.capture.base {
211            Base::Commit(oid) => Some(oid.as_str()),
212            Base::Unborn => None,
213        },
214        empty_tree: input.capture.layout.empty_tree_oid.as_str(),
215        current: onto,
216        prepared: input.prepared.oid.as_str(),
217        signing_key: input.options.signing_key.as_deref(),
218        global_arguments: input.global_args,
219    };
220}
221
222/// What:
223///  The failure a replay that broke maps to.
224/// Why:
225///   A rejected re-run `pre-commit` is Git's own status; any other replay failure becomes a transaction failure
226///   naming the target and the cause, with nothing landed.
227///
228/// In TS you'd write (pseudocode):
229/// ```ts
230/// transactionFailure({ previous: input.settled, message: `${targetRef} moved while this commit was prepared, and replaying it onto ${onto} failed; nothing landed: ${error}` })
231/// ```
232fn replay_failure(capture: &InvocationCapture, onto: &str, cause: String) -> LoopOutcome {
233    return LoopOutcome::Failed {
234        events: vec![PolicyEvent::EngineFailure {
235            code: super::diagnostics::EngineFailureCode::TransactionFailed,
236            message: format!(
237                "{} moved while this commit was prepared, and replaying it onto {onto} failed; nothing landed: {cause}",
238                capture.layout.target_ref
239            ),
240            trigger: Some(super::policy_trigger::Trigger::PreForward),
241            policy: None,
242            path: None,
243        }],
244        exit_code: 2,
245    };
246}
247
248/// What:
249///  The events of a failure after some attempts: the settled events, then what the loop gathered, then the last ones.
250/// Why:
251///   Every failure ends the same list of events.
252///
253/// In TS you'd write (pseudocode):
254/// ```ts
255/// appendEvents({ events: settled.events, appended: [...candidate.events, finding] })
256/// ```
257fn failed_events(settled: &[PolicyEvent], raced: &[PolicyEvent], last: Vec<PolicyEvent>) -> Vec<PolicyEvent> {
258    let mut events: Vec<PolicyEvent> = settled.to_vec();
259    events.extend(raced.iter().cloned());
260    events.extend(last);
261    return events;
262}
263
264/// What:
265///  Replay after a lost race: merge, conflict handling, revalidation, and the rebuilt commit.
266/// Why:
267///   The next candidate is built from the prepared commit again, with the lost race's events kept.
268///
269/// In TS you'd write (pseudocode):
270/// ```ts
271/// const next = await replayCandidate({ input, candidate, onto });
272/// ```
273fn replay_candidate<F: RepositoryFacts>(
274    input: &mut LoopInput<'_, F>,
275    candidate: &Candidate,
276    raced: Vec<PolicyEvent>,
277    onto: &str,
278) -> Result<Result<Candidate, LoopOutcome>, TransactionStop> {
279    let replay: u64 = lost_races_after(candidate.lost_races);
280    let merge: Result<MergeStep, ReplayError> = merge_replay(&replay_request(input, replay, onto));
281    let merged_tree: String = match merge {
282        Err(error) => return Ok(Err(replay_failure(input.capture, onto, replay_error_text(&error)))),
283        Ok(MergeStep::Conflict(conflict)) => {
284            // Unkept: the prepared commit survives the shadow's removal until gc expires unreachable objects.
285            migrate_shadow_objects(
286                input.invocation.context,
287                input.invocation.cwd,
288                input.workspace.landing.shadow.as_path(),
289                input.prepared.oid.as_str(),
290                &input.capture.base,
291                None,
292            )?;
293            let last: Vec<PolicyEvent> = vec![PolicyEvent::ReplayConflict {
294                target_ref: input.capture.layout.target_ref.clone(),
295                paths: conflict
296                    .paths
297                    .iter()
298                    .map(|path| { return EventPath::from_git_bytes(path.as_slice()); })
299                    .collect(),
300                winning_oid: conflict.winning_oid,
301                prepared_oid: conflict.prepared_oid,
302            }];
303            return Ok(Err(LoopOutcome::Failed {
304                events: failed_events(input.settled_events.as_slice(), raced.as_slice(), last),
305                exit_code: 1,
306            }));
307        }
308        Ok(MergeStep::Merged { tree, subsumed_paths, kept_landed_paths }) => {
309            debug(
310                TAG,
311                format!("replay onto {onto} keeps the prepared entries of {subsumed_paths:?} and the landed entries of {kept_landed_paths:?}").as_str(),
312            );
313            tree
314        }
315    };
316    let revalidation: Revalidation = {
317        let context: RevalidationContext<'_> = RevalidationContext {
318            invocation: input.invocation,
319            capture: input.capture,
320            read_only_selection: input.read_only_selection,
321            global_args: input.global_args,
322            no_verify: input.options.no_verify,
323        };
324        revalidate_replay(
325            &context,
326            input.workspace,
327            input.request,
328            input.checks,
329            replay,
330            merged_tree.as_str(),
331            onto,
332            candidate.approved_tree.as_str(),
333        )?
334    };
335    let (tree, index, revalidation_events, revalidated_paths, revalidated_hooks): (String, PathBuf, Vec<PolicyEvent>, Vec<Vec<u8>>, HookChanges) =
336        match revalidation {
337            Revalidation::Blocked { events, exit_code } => {
338                return Ok(Err(LoopOutcome::Failed {
339                    events: failed_events(input.settled_events.as_slice(), raced.as_slice(), events),
340                    exit_code,
341                }));
342            }
343            Revalidation::Revalidated {
344                tree,
345                index,
346                events,
347                committed_paths,
348                hook_changes,
349            } => (tree, index, events, committed_paths, hook_changes),
350        };
351    let rebuilt = match rebuild_replay(&replay_request(input, replay, onto), tree.as_str()) {
352        Ok(found) => found,
353        Err(error) => return Ok(Err(replay_failure(input.capture, onto, replay_error_text(&error)))),
354    };
355    let committed_paths: Vec<Vec<u8>> = union_paths(input.committed_paths.as_slice(), revalidated_paths);
356    let mut worktree_records: Vec<AddedPath> = retarget_records(input.invocation, index.as_path(), input.worktree_records.as_slice())?;
357    worktree_records.extend(revalidated_hooks.worktree_records.iter().cloned());
358    let mut events: Vec<PolicyEvent> = replayed_events(
359        raced,
360        input.prepared.oid.as_str(),
361        &input.capture.base,
362        onto,
363        &rebuilt,
364    );
365    events.extend(revalidation_events);
366    return Ok(Ok(Candidate {
367        new_oid: rebuilt.oid,
368        tree: tree.clone(),
369        expected_old: Base::Commit(String::from(onto)),
370        index: index.clone(),
371        exact_private_index: false,
372        approved_tree: tree,
373        committed_paths,
374        added_paths: retarget_records(input.invocation, index.as_path(), input.added_paths.as_slice())?,
375        worktree_records,
376        hook_changes: HookChanges::combine(&input.hook_changes, &revalidated_hooks),
377        events,
378        attempt: next_attempt_number(candidate.attempt),
379        lost_races: lost_races_after(candidate.lost_races),
380    }));
381}
382
383/// What:
384///  The paths of a replay: those already committed, then the ones revalidation added that are new.
385/// Why:
386///   A path both lists name is one path of the commit.
387///
388/// In TS you'd write (pseudocode):
389/// ```ts
390/// [...new Set([...committedPaths, ...revalidatedPaths])]
391/// ```
392pub fn union_paths(committed: &[Vec<u8>], revalidated: Vec<Vec<u8>>) -> Vec<Vec<u8>> {
393    let mut paths: Vec<Vec<u8>> = committed.to_vec();
394    for path in revalidated {
395        if !paths.contains(&path) {
396            paths.push(path);
397        }
398    }
399    return paths;
400}
401
402/// What:
403///  The events of one replay: the lost race, the replayed commit, and the dropped signature headers when there are some.
404/// Why:
405///   The incumbent prints them in this order, and the headers event only when the rebuild dropped a header.
406///
407/// In TS you'd write (pseudocode):
408/// ```ts
409/// [raceLost, commitReplayed, ...(dropped.length > 0 ? [replayHeadersDropped] : [])]
410/// ```
411pub fn replayed_events(
412    raced: Vec<PolicyEvent>,
413    prepared_oid: &str,
414    base: &Base,
415    onto: &str,
416    rebuilt: &ReplayedCommit,
417) -> Vec<PolicyEvent> {
418    let mut events: Vec<PolicyEvent> = raced;
419    events.push(PolicyEvent::CommitReplayed {
420        prepared_oid: String::from(prepared_oid),
421        from_base: match base {
422            Base::Commit(oid) => Some(oid.clone()),
423            Base::Unborn => None,
424        },
425        onto: String::from(onto),
426        oid: rebuilt.oid.clone(),
427    });
428    if !rebuilt.dropped_headers.is_empty() {
429        events.push(PolicyEvent::ReplayHeadersDropped {
430            prepared_oid: String::from(prepared_oid),
431            oid: rebuilt.oid.clone(),
432            headers: rebuilt.dropped_headers.clone(),
433        });
434    }
435    return events;
436}
437
438/// What:
439///  The landing attempt number that follows a lost race.
440/// Why:
441///   The attempt number names the attempt's landing files, so two attempts of one commit never share a name.
442///
443/// In TS you'd write (pseudocode):
444/// ```ts
445/// attempt + 1
446/// ```
447pub fn next_attempt_number(attempt: i64) -> i64 {
448    return attempt + 1;
449}
450
451/// What:
452///  The count of lost races once one more race is lost.
453/// Why:
454///   The reservation threshold and the `race-lost` marker occurrence both read this count.
455///
456/// In TS you'd write (pseudocode):
457/// ```ts
458/// lostRaces + 1
459/// ```
460pub fn lost_races_after(lost_races: u64) -> u64 {
461    return lost_races + 1;
462}
463
464/// What:
465///  The wording of a replay step failure.
466/// Why:
467///   The transaction failure names the cause after the target and the parent.
468///
469/// In TS you'd write (pseudocode):
470/// ```ts
471/// caughtValueText(error)
472/// ```
473fn replay_error_text(error: &ReplayError) -> String {
474    return match error {
475        ReplayError::Shared(inner) => format!("{inner:?}"),
476        ReplayError::Capture(inner) => format!("{inner:?}"),
477        ReplayError::Subsumption(inner) => format!("{inner:?}"),
478        ReplayError::Merge(inner) => format!("{inner:?}"),
479        ReplayError::Git(inner) => inner.0.clone(),
480        ReplayError::Object(text) => text.clone(),
481    };
482}
483
484/// What:
485///  Map a reservation failure to the transaction failure it is.
486/// Why:
487///   A reservation failure stops the landing loop with nothing landed.
488///
489/// In TS you'd write (pseudocode):
490/// ```ts
491/// throw error
492/// ```
493fn reservation_stop(error: ReservationError) -> TransactionStop {
494    return failed(format!("the landing reservation failed: {error}"));
495}
496
497/// What:
498///  Land a prepared commit, replaying and revalidating after every lost race.
499/// Why:
500///   The landing lock is taken for one attempt only and released before any replay or revalidation work, so a loser
501///   never blocks the others while it rebuilds its commit.
502///
503/// In TS you'd write (pseudocode):
504/// ```ts
505/// await landWithReplay({ ... });
506/// ```
507pub fn land_with_replay<F: RepositoryFacts>(mut input: LoopInput<'_, F>) -> Result<LoopOutcome, TransactionStop> {
508    let layout = &input.capture.layout;
509    let nested: bool = landing_is_nested(layout, input.invocation.environment);
510    let mut reservation: LandingReservation = LandingReservation::open(
511        layout.registry_root.as_path(),
512        input.workspace.landing.directory.as_path(),
513        input.workspace.landing.transaction_id.as_str(),
514        nested,
515    );
516    let result: Result<LoopOutcome, TransactionStop> = run_loop(&mut input, &mut reservation);
517    // Released when the loop ends, landed or failed.
518    if let Err(error) = reservation.release() {
519        debug(TAG, format!("releasing the landing reservation failed: {error:?}").as_str());
520    }
521    return result;
522}
523
524/// What:
525///  The loop proper, with the reservation owned by the caller.
526/// Why:
527///   The caller releases the reservation whatever the loop returns.
528///
529/// In TS you'd write (pseudocode):
530/// ```ts
531/// for (let candidate = ...;;) { ... }
532/// ```
533fn run_loop<F: RepositoryFacts>(
534    input: &mut LoopInput<'_, F>,
535    reservation: &mut LandingReservation,
536) -> Result<LoopOutcome, TransactionStop> {
537    let mut candidate: Candidate = Candidate {
538        new_oid: input.prepared.oid.clone(),
539        tree: input.prepared.tree_oid.clone(),
540        expected_old: input.capture.base.clone(),
541        index: input.workspace.landing.commit_index.clone(),
542        exact_private_index: true,
543        approved_tree: input.prepared.tree_oid.clone(),
544        committed_paths: input.committed_paths.clone(),
545        added_paths: input.added_paths.clone(),
546        worktree_records: input.worktree_records.clone(),
547        hook_changes: input.hook_changes.clone(),
548        events: Vec::new(),
549        attempt: 1,
550        lost_races: 0,
551    };
552    loop {
553        let payload: LandingPayload = LandingPayload::Commit {
554            new_oid: candidate.new_oid.clone(),
555            landed_tree_oid: candidate.tree.clone(),
556            expected_old: candidate.expected_old.clone(),
557            landed_index: candidate.index.clone(),
558            exact_private_index: candidate.exact_private_index,
559            guarded_paths: candidate.hook_changes.paths.clone(),
560        };
561        let request: LandingRequest<'_> = LandingRequest {
562            capture: input.capture,
563            mode: input.mode,
564            payload: &payload,
565            committed_paths: candidate.committed_paths.as_slice(),
566            added_paths: candidate.added_paths.as_slice(),
567            selected_worktree_paths: candidate.worktree_records.as_slice(),
568            index_lock_timeout_ms: input.index_lock_timeout_ms,
569            attempt: candidate.attempt,
570        };
571        let outcome: LandingOutcome = {
572            // The lock is released when this block ends, before any replay work.
573            let lock: OwnerLock = acquire_reserved_lock(
574                input.invocation.context,
575                input.invocation.cwd,
576                &input.capture.layout,
577                input.invocation.environment,
578                reservation,
579            )
580            .map_err(reservation_stop)?;
581            land_transaction(
582                input.invocation.context,
583                input.invocation.cwd,
584                &lock,
585                &mut input.workspace.landing,
586                &request,
587            )?
588        };
589        let onto: String = match &outcome {
590            LandingOutcome::Landed { oid } => {
591                return Ok(LoopOutcome::Landed {
592                    oid: oid.clone(),
593                    events: candidate.events,
594                    worktree_records: candidate.worktree_records,
595                    added_paths: candidate.added_paths,
596                });
597            }
598            LandingOutcome::HeadMoved {
599                current: Base::Commit(oid),
600                replayable: true,
601            } => oid.clone(),
602            _ => {
603                let mut last: Vec<PolicyEvent> = Vec::new();
604                if let Some(event) = landing_finding(&outcome, input.capture, Some(input.prepared.oid.as_str()), false) {
605                    last.push(event);
606                }
607                return Ok(LoopOutcome::Failed {
608                    events: failed_events(input.settled_events.as_slice(), candidate.events.as_slice(), last),
609                    exit_code: LANDING_FINDING_EXIT_CODE,
610                });
611            }
612        };
613        let lost_races: u64 = lost_races_after(candidate.lost_races);
614        debug(TAG, format!("lost landing race {lost_races} to {onto}").as_str());
615        let reserved: bool = lost_races >= input.reserve_after_lost_races
616            && reservation.reserve(lost_races).map_err(reservation_stop)?;
617        reach_transaction_phase(Phase::RaceLost, Some(lost_races))?;
618        let mut raced: Vec<PolicyEvent> = candidate.events.clone();
619        raced.push(PolicyEvent::LandingRaceLost {
620            attempt: lost_races,
621            winning_oid: onto.clone(),
622        });
623        if reserved {
624            raced.push(PolicyEvent::LandingReserved { lost_races });
625        }
626        match replay_candidate(input, &candidate, raced, onto.as_str())? {
627            Ok(next) => candidate = next,
628            Err(ended) => return Ok(ended),
629        }
630    }
631}
632
633/// Controls for this module stay out of the release executable.
634#[cfg(test)]
635#[path = "commit_landing_loop_tests.rs"]
636mod tests;