Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .codegraph/codegraph.db
Binary file not shown.
1 change: 1 addition & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ boundaries above remain the target modular MSA architecture.
| `tepp_simulation` | known-truth temporal/event data generation |
| `validation_core` | RMSE, bias, coverage, graph, and Monte Carlo metrics |
| `tepp_api` | versioned DTO, schema, and export contracts |
| `system_clock` | system time cannot be replaced by event, assertion, document, available, or cutoff time |
| `event_clock` | event time cannot be replaced by assertion, system, document, or available time |
| `assertion_clock` | assertion time cannot be replaced by event, system, document, or available time |
| `cutoff_clock` | knowledge cutoff cannot be replaced by event, system, or availability time |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Comment thread
seonghobae marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to TEPP are documented here. The format follows Keep a Chang

### Added

- `system_clock` identity gate: event, assertion, document, availability, and knowledge-cutoff time cannot stand in for system time; recovered system stamps match known truth at a higher computed rate than treating every stamp as event time (ADR 0002).
- `event_clock` identity gate: assertion, system, document, and availability time cannot stand in for event/valid time; recovered event stamps match known truth at a higher computed rate than treating every stamp as assertion time (ADR 0002).
- `assertion_clock` identity gate: event, system, document, and availability time cannot stand in for assertion time; recovered assertion stamps match known truth at a higher computed rate than treating every stamp as event time (ADR 0002).
- `cutoff_clock` identity gate: event time, system time, and availability time cannot stand in for knowledge cutoff; recovered cutoff stamps match known truth at a higher computed rate than treating every stamp as availability time (ADR 0002).
Expand Down
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"crates/tepp_simulation",
"crates/validation_core",
"crates/tepp_api",
"crates/system_clock",
"crates/event_clock",
"crates/assertion_clock",
"crates/cutoff_clock",
Expand Down Expand Up @@ -44,6 +45,7 @@ default-members = [
"crates/tepp_simulation",
"crates/validation_core",
"crates/tepp_api",
"crates/system_clock",
"crates/event_clock",
"crates/assertion_clock",
"crates/cutoff_clock",
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ crates/corpus_split
crates/tepp_simulation
crates/validation_core
crates/tepp_api
crates/system_clock
crates/event_clock
crates/assertion_clock
crates/cutoff_clock
Expand Down
17 changes: 17 additions & 0 deletions crates/system_clock/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "system_clock"
description = "System time cannot be replaced by event, assertion, document, available, or cutoff time."
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
homepage.workspace = true
readme.workspace = true
keywords.workspace = true
categories.workspace = true
publish = false

[lints]
workspace = true
145 changes: 145 additions & 0 deletions crates/system_clock/src/clock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//! Clock-family identity for system stamps.

use crate::SystemClockError;

/// Closed vocabulary of clocks that must not be confused with system time.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ClockFamily {
/// Event/valid time.
EventTime,
/// Assertion time.
AssertionTime,
/// Document creation or revision time.
DocumentTime,
/// Availability time.
AvailableTime,
/// Knowledge-cutoff time.
CutoffTime,
/// System/record time.
SystemTime,
}

/// Return whether a stamp is on the system clock.
///
/// # Errors
///
/// This function is infallible for the closed vocabulary and exists to keep
/// the public comparison surface explicit.
#[allow(clippy::unnecessary_wraps)]
pub fn stamp_is_system(family: ClockFamily) -> Result<bool, SystemClockError> {
Ok(matches!(family, ClockFamily::SystemTime))
}

/// Refuse to treat event time as system time.
///
/// # Errors
///
/// Always returns [`SystemClockError::EventTimeIsNotSystemTime`].
pub fn refuse_event_time_as_system() -> Result<(), SystemClockError> {
Err(SystemClockError::EventTimeIsNotSystemTime)
}

/// Refuse to treat assertion time as system time.
///
/// # Errors
///
/// Always returns [`SystemClockError::AssertionTimeIsNotSystemTime`].
pub fn refuse_assertion_time_as_system() -> Result<(), SystemClockError> {
Err(SystemClockError::AssertionTimeIsNotSystemTime)
}

/// Refuse to treat document time as system time.
///
/// # Errors
///
/// Always returns [`SystemClockError::DocumentTimeIsNotSystemTime`].
pub fn refuse_document_time_as_system() -> Result<(), SystemClockError> {
Err(SystemClockError::DocumentTimeIsNotSystemTime)
}

/// Refuse to treat availability time as system time.
///
/// # Errors
///
/// Always returns [`SystemClockError::AvailableTimeIsNotSystemTime`].
pub fn refuse_available_time_as_system() -> Result<(), SystemClockError> {
Err(SystemClockError::AvailableTimeIsNotSystemTime)
}

/// Refuse to treat knowledge-cutoff time as system time.
///
/// # Errors
///
/// Always returns [`SystemClockError::CutoffTimeIsNotSystemTime`].
pub fn refuse_cutoff_time_as_system() -> Result<(), SystemClockError> {
Err(SystemClockError::CutoffTimeIsNotSystemTime)
}
Comment thread
seonghobae marked this conversation as resolved.

/// Fraction of recovered system-clock flags that match known truth.
///
/// # Errors
///
/// Returns [`SystemClockError::InvalidSystemPayload`] when either slice is
/// empty or the lengths differ.
pub fn identity_recovery_rate(truth: &[bool], decided: &[bool]) -> Result<f64, SystemClockError> {
if truth.is_empty() || truth.len() != decided.len() {
return Err(SystemClockError::InvalidSystemPayload);
}
let mut matches = 0_usize;
for (truth_flag, decided_flag) in truth.iter().zip(decided) {
if truth_flag == decided_flag {
matches += 1;
}
}
Ok(matches as f64 / truth.len() as f64)
Comment thread
seonghobae marked this conversation as resolved.
Comment thread
seonghobae marked this conversation as resolved.
}

#[cfg(test)]
mod tests {
use super::{
ClockFamily, identity_recovery_rate, refuse_assertion_time_as_system,
refuse_available_time_as_system, refuse_cutoff_time_as_system,
refuse_document_time_as_system, refuse_event_time_as_system, stamp_is_system,
};
use crate::SystemClockError;

#[test]
fn local_branches_cover_families_and_payloads() {
assert!(stamp_is_system(ClockFamily::SystemTime).expect("system"));
assert!(!stamp_is_system(ClockFamily::EventTime).expect("event"));
assert!(!stamp_is_system(ClockFamily::AssertionTime).expect("assertion"));
assert!(!stamp_is_system(ClockFamily::DocumentTime).expect("document"));
assert!(!stamp_is_system(ClockFamily::AvailableTime).expect("available"));
assert!(!stamp_is_system(ClockFamily::CutoffTime).expect("cutoff"));
assert_eq!(
refuse_event_time_as_system(),
Err(SystemClockError::EventTimeIsNotSystemTime)
);
assert_eq!(
refuse_assertion_time_as_system(),
Err(SystemClockError::AssertionTimeIsNotSystemTime)
);
assert_eq!(
refuse_document_time_as_system(),
Err(SystemClockError::DocumentTimeIsNotSystemTime)
);
assert_eq!(
refuse_available_time_as_system(),
Err(SystemClockError::AvailableTimeIsNotSystemTime)
);
assert_eq!(
refuse_cutoff_time_as_system(),
Err(SystemClockError::CutoffTimeIsNotSystemTime)
);
let matched = identity_recovery_rate(&[true], &[true]).expect("rate");
assert!((matched - 1.0).abs() < f64::EPSILON);
assert_eq!(
identity_recovery_rate(&[], &[]),
Err(SystemClockError::InvalidSystemPayload)
);
assert_eq!(
identity_recovery_rate(&[true], &[]),
Err(SystemClockError::InvalidSystemPayload)
);
}
}
74 changes: 74 additions & 0 deletions crates/system_clock/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Fail-closed system-clock errors.

use std::fmt;

/// A fail-closed system-clock error.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum SystemClockError {
/// Event time was treated as system time.
EventTimeIsNotSystemTime,
/// Assertion time was treated as system time.
AssertionTimeIsNotSystemTime,
/// Document time was treated as system time.
DocumentTimeIsNotSystemTime,
/// Availability time was treated as system time.
AvailableTimeIsNotSystemTime,
/// Knowledge-cutoff time was treated as system time.
CutoffTimeIsNotSystemTime,
/// A recovery slice was empty or length-mismatched.
InvalidSystemPayload,
}

impl fmt::Display for SystemClockError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
let message = match self {
Self::EventTimeIsNotSystemTime => "event time is not system time",
Self::AssertionTimeIsNotSystemTime => "assertion time is not system time",
Self::DocumentTimeIsNotSystemTime => "document time is not system time",
Self::AvailableTimeIsNotSystemTime => "availability time is not system time",
Self::CutoffTimeIsNotSystemTime => "knowledge cutoff is not system time",
Self::InvalidSystemPayload => "invalid system-clock payload",
};
formatter.write_str(message)
}
}

impl std::error::Error for SystemClockError {}

#[cfg(test)]
mod tests {
use super::SystemClockError;

#[test]
fn error_messages_are_stable() {
for (error, message) in [
(
SystemClockError::EventTimeIsNotSystemTime,
"event time is not system time",
),
(
SystemClockError::AssertionTimeIsNotSystemTime,
"assertion time is not system time",
),
(
SystemClockError::DocumentTimeIsNotSystemTime,
"document time is not system time",
),
(
SystemClockError::AvailableTimeIsNotSystemTime,
"availability time is not system time",
),
(
SystemClockError::CutoffTimeIsNotSystemTime,
"knowledge cutoff is not system time",
),
(
SystemClockError::InvalidSystemPayload,
"invalid system-clock payload",
),
] {
assert_eq!(error.to_string(), message);
}
}
}
29 changes: 29 additions & 0 deletions crates/system_clock/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![allow(clippy::cast_precision_loss)]
//! System time cannot be replaced by the other TEPP clocks.
//!
//! System/record time is when TEPP recorded a change. Event, assertion,
//! document, available, and cutoff times are not substitutes (ADR 0002).

mod clock;
mod error;

/// Closed vocabulary of clocks that must not be confused with system time.
pub use clock::ClockFamily;
/// Fraction of recovered system-clock flags that match known truth.
pub use clock::identity_recovery_rate;
/// Refuse to treat assertion time as system time.
pub use clock::refuse_assertion_time_as_system;
/// Refuse to treat availability time as system time.
pub use clock::refuse_available_time_as_system;
/// Refuse to treat knowledge-cutoff time as system time.
pub use clock::refuse_cutoff_time_as_system;
/// Refuse to treat document time as system time.
pub use clock::refuse_document_time_as_system;
/// Refuse to treat event time as system time.
pub use clock::refuse_event_time_as_system;
/// Return whether a stamp is on the system clock.
pub use clock::stamp_is_system;
/// Fail-closed system-clock errors.
pub use error::SystemClockError;
7 changes: 7 additions & 0 deletions crates/system_clock/tests/crate_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Integration contract for the `system_clock` package identity.

#[test]
fn package_identity_is_stable() {
let observed = std::hint::black_box(env!("CARGO_PKG_NAME"));
assert_eq!(observed, "system_clock");
}
Loading
Loading