diff --git a/.codegraph/codegraph.db b/.codegraph/codegraph.db new file mode 100644 index 000000000..3bb262c9f Binary files /dev/null and b/.codegraph/codegraph.db differ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c4520864e..5ca70db53 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -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 | diff --git a/CHANGELOG.md b/CHANGELOG.md index e890dbb02..6d7118bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). diff --git a/Cargo.lock b/Cargo.lock index c3c85201c..f9792887c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1492,6 +1492,10 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "system_clock" +version = "0.1.0" + [[package]] name = "temporal_core" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 0b396b708..40e043a1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", @@ -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", diff --git a/README.md b/README.md index 6bfe25f4c..afb16cda7 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/crates/system_clock/Cargo.toml b/crates/system_clock/Cargo.toml new file mode 100644 index 000000000..edaf58543 --- /dev/null +++ b/crates/system_clock/Cargo.toml @@ -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 diff --git a/crates/system_clock/src/clock.rs b/crates/system_clock/src/clock.rs new file mode 100644 index 000000000..a5489a42b --- /dev/null +++ b/crates/system_clock/src/clock.rs @@ -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 { + 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) +} + +/// 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 { + 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) +} + +#[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) + ); + } +} diff --git a/crates/system_clock/src/error.rs b/crates/system_clock/src/error.rs new file mode 100644 index 000000000..9c9a15ae3 --- /dev/null +++ b/crates/system_clock/src/error.rs @@ -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); + } + } +} diff --git a/crates/system_clock/src/lib.rs b/crates/system_clock/src/lib.rs new file mode 100644 index 000000000..fb2e8da32 --- /dev/null +++ b/crates/system_clock/src/lib.rs @@ -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; diff --git a/crates/system_clock/tests/crate_contract.rs b/crates/system_clock/tests/crate_contract.rs new file mode 100644 index 000000000..499087c54 --- /dev/null +++ b/crates/system_clock/tests/crate_contract.rs @@ -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"); +} diff --git a/crates/system_clock/tests/system_clock_contract.rs b/crates/system_clock/tests/system_clock_contract.rs new file mode 100644 index 000000000..32902ed33 --- /dev/null +++ b/crates/system_clock/tests/system_clock_contract.rs @@ -0,0 +1,82 @@ +//! Other TEPP clocks cannot stand in for system time. + +use system_clock::{ + ClockFamily, SystemClockError, 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, +}; + +#[test] +fn other_clocks_cannot_stand_in_for_system_time() { + 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) + ); + 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")); +} + +#[test] +fn recovered_system_stamps_match_known_truth_better_than_event_stand_in() { + let recovered = [ + ClockFamily::SystemTime, + ClockFamily::EventTime, + ClockFamily::AssertionTime, + ClockFamily::DocumentTime, + ClockFamily::AvailableTime, + ClockFamily::CutoffTime, + ]; + let recovered_flags = recovered.map(|family| stamp_is_system(family).expect("recovered")); + let collapsed_flags = [false; 6]; + let truth_flags = [true, false, false, false, false, false]; + let recovered_rate = identity_recovery_rate(&truth_flags, &recovered_flags).expect("ok"); + let collapsed_rate = identity_recovery_rate(&truth_flags, &collapsed_flags).expect("bad"); + let expected = { + let mut matches = 0_usize; + for (truth_flag, decided_flag) in truth_flags.iter().zip(recovered_flags.iter()) { + if truth_flag == decided_flag { + matches += 1; + } + } + f64::from(u32::try_from(matches).expect("test count fits")) + / f64::from(u32::try_from(truth_flags.len()).expect("len")) + }; + assert!((recovered_rate - expected).abs() < f64::EPSILON); + assert!(recovered_rate > collapsed_rate); +} + +#[test] +fn empty_or_mismatched_identity_payloads_fail_closed() { + assert_eq!( + identity_recovery_rate(&[], &[]), + Err(SystemClockError::InvalidSystemPayload) + ); + assert_eq!( + identity_recovery_rate(&[true], &[]), + Err(SystemClockError::InvalidSystemPayload) + ); + assert_eq!( + identity_recovery_rate(&[true, false], &[true]), + Err(SystemClockError::InvalidSystemPayload) + ); +} diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 5c09533e3..c21e25f5b 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -10,7 +10,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | immutable source evidence and exact spans | PRD; Architecture; ADR 0008 | `evidence_core`, Task 2 tests/doctoring; `persistence_postgres` source-artifact SQL insert/lookup plus idempotent retry (#40 implemented-main); typed `text_segment` byte-span SQL (active PR) | implemented-main | | Rust numerical authority / CPU `f64` reference | ADR 0001 | current workspace foundation; future estimators | partial | | Rust workspace/quality foundation | ADR 0007 | workspace/CI/repository contract | implemented-main | -| six distinct clocks and uncertain intervals | PRD; ADR 0002 | PR #8 `temporal_core` on protected main; `event_clock` event-vs-assertion/system/document/available identity on the active PR | active-PR | +| six distinct clocks and uncertain intervals | PRD; ADR 0002 | PR #8 `temporal_core` on protected main; `system_clock` system-vs-other-clock identity on the active PR | active-PR | | Allen relation algebra/bounded closure | ADR 0002; temporal research | PR #9 `temporal_core` path-consistency on protected main | implemented-main | | forward-only transition subgraph | PRD; ADR 0002/0003 | `relation_graph` on protected main; `citation_edge` provenance-vs-transition gate on the active PR | active-PR | | event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; `persistence_postgres` mention SQL implemented-main refuses mention-as-instance; event-instance SQL (#39 implemented-main) refuses inverted windows; full intelligence stack remaining | partial | diff --git a/docs/adr/0002-six-clock-temporal-semantics.md b/docs/adr/0002-six-clock-temporal-semantics.md index 91e84fd8f..8980e6c31 100644 --- a/docs/adr/0002-six-clock-temporal-semantics.md +++ b/docs/adr/0002-six-clock-temporal-semantics.md @@ -1,6 +1,7 @@ # ADR 0002 — Six-clock temporal semantics and leakage prevention **Decision status:** Accepted +**Implementation maturity:** active-PR — system-clock identity in `system_clock` on the active PR; remaining graph/split enforcement stays accepted-target **Implementation maturity:** active-PR — event-clock identity in `event_clock` on the active PR; remaining graph/split enforcement stays accepted-target **Implementation maturity:** active-PR — assertion-clock identity in `assertion_clock` on the active PR; remaining graph/split enforcement stays accepted-target **Implementation maturity:** active-PR — knowledge-cutoff identity in `cutoff_clock` on the active PR; remaining graph/split enforcement stays accepted-target diff --git a/docs/adr/README.md b/docs/adr/README.md index 374647a73..c1ae8a593 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -7,6 +7,7 @@ Read [`ADR_POLICY.md`](ADR_POLICY.md) first. **Decision status and implementatio | ADR | Decision | Decision status | Implementation maturity | Clarification / supersession | |---|---|---|---|---| | [0001](0001-rust-first-modular-msa.md) | Rust-first numerical core and CPU `f64` reference | Accepted | partial | ADR 0011 owns cross-service/MSA authority; 0001 retains numerical/backend authority. | +| [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | active-PR | System-clock identity in `system_clock` on the active PR; remaining graph/split enforcement stays accepted-target. | | [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | active-PR | Event-clock identity in `event_clock` on the active PR; remaining graph/split enforcement stays accepted-target. | | [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | active-PR | Assertion-clock identity in `assertion_clock` on the active PR; remaining graph/split enforcement stays accepted-target. | | [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | active-PR | Knowledge-cutoff identity is `cutoff_clock` on the active PR; typed clocks/intervals remain implemented-main via `temporal_core`. Later graph/split enforcement remains target work. | diff --git a/docs/research/standards-and-literature.md b/docs/research/standards-and-literature.md index 4fcfa933d..c7bec0f74 100644 --- a/docs/research/standards-and-literature.md +++ b/docs/research/standards-and-literature.md @@ -151,6 +151,8 @@ Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL* Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL*. Morgan Kaufmann. Valid vs transaction time informs `event_clock`; event time is TEPP's valid-time clock. +Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL*. Morgan Kaufmann. Transaction time informs `system_clock`; it is not event, assertion, document, available, or cutoff time. + ## Privacy lifecycle, retention, and legal hold European Union. (2016). *Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 on the protection of natural persons with regard to the processing of personal data and on the free movement of such data (General Data Protection Regulation)*. Official Journal of the European Union, L 119, 1–88. https://eur-lex.europa.eu/eli/reg/2016/679/oj diff --git a/docs/research/system-clock-identity.md b/docs/research/system-clock-identity.md new file mode 100644 index 000000000..557dd88f8 --- /dev/null +++ b/docs/research/system-clock-identity.md @@ -0,0 +1,27 @@ +# System-clock identity (doctoring) + +## Scope + +`system_clock` keeps system/record time distinct from event, assertion, +document, availability, and knowledge-cutoff time. Recovery is the +computed share of all system/non-system classifications that match known +truth. + +This slice does not persist clocks or recreate `document_clocks`, +`available_clock`, `cutoff_clock`, `assertion_clock`, or `event_clock`. + +## Authority + +### Normative TEPP contract + +- `docs/adr/0002-six-clock-temporal-semantics.md` — system time is when + TEPP recorded a change; it is not event time or availability time. + +### Supporting literature + +Snodgrass (2000) treats transaction time as the time a fact was recorded. +That is the TEPP system clock. Valid time and other TEPP clocks are not +substitutes. + +Snodgrass, R. T. (2000). *Developing time-oriented database applications +in SQL*. Morgan Kaufmann. diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index 2ae7d5b67..3ec23730a 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -29,6 +29,7 @@ This report tracks exact-head scientific and engineering evidence required befor | Mention-confidence Brier score | `event_core` | active-PR | calibration vs binary truth | perfect 0 / half 0.25 RMSE | ADR 0003; `docs/research/mention-confidence-brier.md` | | Checkpoint is not the estimator | `checkpoint_authority` | accepted-target | active PR | refuse checkpoint-as-estimator + unvalidated artifact + recovery vs estimator collapse | ADR 0001/0014 | | Versioned API/export contracts | `tepp_api` | implemented-main | naruon HTTP interchange | unknown-field/version/limit + naruon HTTPS interchange tests | Task 12 / PR #21; live HTTP service remaining | +| System-clock identity | `system_clock` | active-PR | this PR | recovered system flags vs event-time stand-in | ADR 0002 | | Event-clock identity | `event_clock` | active-PR | this PR | recovered event flags vs assertion-time stand-in | ADR 0002 | | Assertion-clock identity | `assertion_clock` | active-PR | this PR | recovered assertion flags vs event-time stand-in | ADR 0002 | | Availability-clock identity | `available_clock` | active-PR | this PR | recovered availability flags vs system-time stand-in | ADR 0002 | diff --git a/scripts/check_workspace_contract.py b/scripts/check_workspace_contract.py index 5c278efb2..6b7432e1f 100644 --- a/scripts/check_workspace_contract.py +++ b/scripts/check_workspace_contract.py @@ -23,6 +23,7 @@ "tepp_simulation", "validation_core", "tepp_api", + "system_clock", "event_clock", "assertion_clock", "cutoff_clock",