metrale_bench/gate/
amnesty.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: Content-pinned amnesty: a table of paths whose exact blob at the
4//! head excuses them from invalidating gate records.
5//!
6//! `check_paths::invalidating_paths` drops a changed path only when
7//! `excused` says the path is listed in `ONE_TIME_AMNESTY` and its blob at
8//! the head is the pinned 40-hex OID; each drop is logged with
9//! `tracing::warn!`. Any later edit changes the OID, so the path invalidates
10//! again. The production table is empty, so today nothing is excused.
11//!
12//! This file is in `coverage::BOUNDARY_FILES`: editing it re-opens every gate.
13//!
14//! Owner: bench gate.
15//! Invariants:
16//! - `excused_by` returns true only when the path is listed and git reports
17//!   exactly the pinned 40-hex OID for `<head>:<path>`; every git failure is
18//!   `false`.
19
20use std::path::Path;
21
22/// 2026-09-26: One excused path: the file, the exact blob its grant covers,
23/// and why.
24#[derive(Debug, Clone, Copy)]
25pub struct AmnestyEntry {
26    pub path: &'static str,
27    /// 2026-09-26: The 40-hex blob OID the grant covers, as printed by
28    /// `git rev-parse <head>:<path>`. Any value that is not a 40-hex OID
29    /// matches no blob.
30    pub head_blob_oid: &'static str,
31    pub grant: &'static str,
32}
33
34/// 2026-09-26: 2026-08-30T00:00:00Z. A record counts as fresh for the expiry
35/// test only when its `recorded_at` is later than this.
36pub const AMNESTY_EPOCH: u64 = 1_788_048_000;
37
38/// 2026-09-26: The production amnesty table. It is empty.
39///
40/// `amnesty_expires_once_every_gate_has_a_fresh_record` depends on it: with
41/// entries, it fails once every required gate that has a record has one newer
42/// than [`AMNESTY_EPOCH`]; empty, it fails if any such gate's newest record is
43/// not newer. `the_table_is_exactly_the_pr_816_grant` allows only an empty
44/// table or the single path `crates/bench/src/gate/coverage.rs`.
45pub const ONE_TIME_AMNESTY: [AmnestyEntry; 0] = [];
46
47/// 2026-09-26: Whether [`ONE_TIME_AMNESTY`] excuses `path` at `head`.
48pub fn excused(root: &Path, head: &str, path: &str) -> bool {
49    excused_by(root, head, path, &ONE_TIME_AMNESTY)
50}
51
52/// 2026-09-26: [`excused`] against an explicit table, so tests can pin real
53/// OIDs.
54///
55/// True iff `path` is in `table` and the blob at `<head>:<path>` is exactly
56/// the pinned 40-hex OID. Anything else, including any git failure, is
57/// `false`.
58pub fn excused_by(root: &Path, head: &str, path: &str, table: &[AmnestyEntry]) -> bool {
59    let Some(entry) = table.iter().find(|e| e.path == path) else {
60        return false;
61    };
62    let Ok(out) = std::process::Command::new("git")
63        .arg("-C")
64        .arg(root)
65        .args(["rev-parse", &format!("{head}:{path}")])
66        .stdin(std::process::Stdio::null())
67        .output()
68    else {
69        return false;
70    };
71    if !out.status.success() {
72        return false;
73    }
74    let oid = String::from_utf8_lossy(&out.stdout).trim().to_string();
75    oid.len() == 40 && oid.chars().all(|c| c.is_ascii_hexdigit()) && oid == entry.head_blob_oid
76}