diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 0a0bdada3..8edbbf336 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 | +| `inferred_status` | inferred relations cannot be promoted to observed evidence or transitions | | `support_edge` | support, contradiction, summary, and outcome_of edges are not state transitions | | `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 | diff --git a/CHANGELOG.md b/CHANGELOG.md index c704ba4a8..4c6791a02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ All notable changes to TEPP are documented here. The format follows Keep a Chang ### Added +- `inferred_status` identity gate: inferred relations cannot be promoted to observed evidence or to state transitions; recovered observed/inferred labels match known truth at a higher computed rate than treating every status as observed (ADR 0003). +- `persistence_postgres` retention/deletion/legal-hold (migration `0007`): policy rows, legal holds that block completed deletion, evidence tombstones without raw-source restore, analysis exclusion only for `logical_revocation`/`identity_tombstone` (not `cache_export_removal`), and deletion requests bound to the cited retention policy's tenant/class/purpose. - `support_edge` identity gate: support, contradiction, summary, and `outcome_of` edges cannot become state transitions; recovered evidential kinds match known truth at a higher computed rate than collapsing every kind to support (ADR 0002/0003). - `persistence_postgres` retention/deletion/legal-hold (migration `0007`): policy rows, legal holds that block completed deletion, evidence tombstones without raw-source restore, analysis exclusion only for `logical_revocation`/`identity_tombstone` (not `cache_export_removal`), and deletion requests bound to the cited retention policy's tenant/class/purpose. - `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). diff --git a/Cargo.lock b/Cargo.lock index 847934b33..943d47543 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -746,6 +746,10 @@ dependencies = [ "hashbrown 0.17.1", ] +[[package]] +name = "inferred_status" +version = "0.1.0" + [[package]] name = "inout" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index 2fced08b8..4c260c8a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "crates/tepp_simulation", "crates/validation_core", "crates/tepp_api", + "crates/inferred_status", "crates/support_edge", "crates/system_clock", "crates/event_clock", @@ -46,6 +47,7 @@ default-members = [ "crates/tepp_simulation", "crates/validation_core", "crates/tepp_api", + "crates/inferred_status", "crates/support_edge", "crates/system_clock", "crates/event_clock", diff --git a/README.md b/README.md index 5e742e106..cc6cbd536 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ crates/corpus_split crates/tepp_simulation crates/validation_core crates/tepp_api +crates/inferred_status crates/support_edge crates/system_clock crates/event_clock diff --git a/crates/inferred_status/Cargo.toml b/crates/inferred_status/Cargo.toml new file mode 100644 index 000000000..5ac8b9297 --- /dev/null +++ b/crates/inferred_status/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "inferred_status" +description = "Inferred relations cannot be promoted to observed evidence or transitions." +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/inferred_status/src/error.rs b/crates/inferred_status/src/error.rs new file mode 100644 index 000000000..0513f8f96 --- /dev/null +++ b/crates/inferred_status/src/error.rs @@ -0,0 +1,53 @@ +//! Fail-closed inferred-status errors. + +use std::fmt; + +/// A fail-closed inferred-status error. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub enum InferredStatusError { + /// An inferred relation was treated as observed evidence. + InferredIsNotObserved, + /// An inferred relation was treated as a state transition. + InferredIsNotTransition, + /// A recovery slice was empty or length-mismatched. + InvalidStatusPayload, +} + +impl fmt::Display for InferredStatusError { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match self { + Self::InferredIsNotObserved => "inferred relation is not observed evidence", + Self::InferredIsNotTransition => "inferred relation is not a state transition", + Self::InvalidStatusPayload => "invalid inferred-status payload", + }; + formatter.write_str(message) + } +} + +impl std::error::Error for InferredStatusError {} + +#[cfg(test)] +mod tests { + use super::InferredStatusError; + + #[test] + fn error_messages_are_stable() { + for (error, message) in [ + ( + InferredStatusError::InferredIsNotObserved, + "inferred relation is not observed evidence", + ), + ( + InferredStatusError::InferredIsNotTransition, + "inferred relation is not a state transition", + ), + ( + InferredStatusError::InvalidStatusPayload, + "invalid inferred-status payload", + ), + ] { + assert_eq!(error.to_string(), message); + } + } +} diff --git a/crates/inferred_status/src/lib.rs b/crates/inferred_status/src/lib.rs new file mode 100644 index 000000000..0ed6bb635 --- /dev/null +++ b/crates/inferred_status/src/lib.rs @@ -0,0 +1,23 @@ +#![forbid(unsafe_code)] +#![deny(missing_docs)] +#![allow(clippy::cast_precision_loss)] +//! Inferred relations cannot be promoted to observed evidence or transitions. +//! +//! LLM, reasoner, and heuristic proposals stay inferred until deterministic +//! schema, evidence, and scientific validation promote them (ADR 0003). + +mod error; +mod status; + +/// Fail-closed inferred-status errors. +pub use error::InferredStatusError; +/// Closed vocabulary of presence evidence that is not yet a transition. +pub use status::EvidenceStatus; +/// Fraction of recovered evidence statuses that match known truth. +pub use status::identity_recovery_rate; +/// Refuse to treat an inferred relation as observed evidence. +pub use status::refuse_inferred_as_observed; +/// Refuse to treat an inferred relation as a state transition. +pub use status::refuse_inferred_as_transition; +/// Return whether a status is observed evidence. +pub use status::status_is_observed; diff --git a/crates/inferred_status/src/status.rs b/crates/inferred_status/src/status.rs new file mode 100644 index 000000000..0ee4805d9 --- /dev/null +++ b/crates/inferred_status/src/status.rs @@ -0,0 +1,143 @@ +//! Observed versus inferred relation evidence status. + +use crate::InferredStatusError; + +/// Closed vocabulary of presence evidence that is not yet a transition. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EvidenceStatus { + /// Directly observed in source documents or authoritative systems. + Observed, + /// Derived by a model, reasoner, or heuristic and not yet promoted. + Inferred, +} + +impl EvidenceStatus { + /// Return the stable wire status name. + #[must_use] + pub const fn wire_name(self) -> &'static str { + match self { + Self::Observed => "observed", + Self::Inferred => "inferred", + } + } + + /// Parse a stable wire status name. + /// + /// # Errors + /// + /// Returns [`InferredStatusError::InvalidStatusPayload`] for unrecognized + /// names. + pub fn from_wire_name(name: &str) -> Result { + match name { + "observed" => Ok(Self::Observed), + "inferred" => Ok(Self::Inferred), + _ => Err(InferredStatusError::InvalidStatusPayload), + } + } +} + +/// Return whether a status is observed evidence. +/// +/// # Errors +/// +/// This function is infallible for the closed vocabulary and exists to keep +/// the public comparison surface explicit. +#[allow(clippy::unnecessary_wraps)] +pub fn status_is_observed(status: EvidenceStatus) -> Result { + Ok(matches!(status, EvidenceStatus::Observed)) +} + +/// Refuse to treat an inferred relation as observed evidence. +/// +/// # Errors +/// +/// Returns [`InferredStatusError::InferredIsNotObserved`] when `status` is +/// [`EvidenceStatus::Inferred`]. +pub fn refuse_inferred_as_observed(status: EvidenceStatus) -> Result<(), InferredStatusError> { + match status { + EvidenceStatus::Inferred => Err(InferredStatusError::InferredIsNotObserved), + EvidenceStatus::Observed => Ok(()), + } +} + +/// Refuse to treat an inferred relation as a state transition. +/// +/// # Errors +/// +/// Returns [`InferredStatusError::InferredIsNotTransition`] when `status` is +/// [`EvidenceStatus::Inferred`]. +pub fn refuse_inferred_as_transition(status: EvidenceStatus) -> Result<(), InferredStatusError> { + match status { + EvidenceStatus::Inferred => Err(InferredStatusError::InferredIsNotTransition), + EvidenceStatus::Observed => Ok(()), + } +} + +/// Fraction of recovered evidence statuses that match known truth. +/// +/// # Errors +/// +/// Returns [`InferredStatusError::InvalidStatusPayload`] when either slice is +/// empty or the lengths differ. +pub fn identity_recovery_rate( + truth: &[EvidenceStatus], + decided: &[EvidenceStatus], +) -> Result { + if truth.is_empty() || truth.len() != decided.len() { + return Err(InferredStatusError::InvalidStatusPayload); + } + let mut matches = 0_u32; + for (truth_status, decided_status) in truth.iter().zip(decided) { + if truth_status == decided_status { + matches += 1; + } + } + Ok(f64::from(matches) / truth.len() as f64) +} + +#[cfg(test)] +mod tests { + use super::{ + EvidenceStatus, identity_recovery_rate, refuse_inferred_as_observed, + refuse_inferred_as_transition, status_is_observed, + }; + use crate::InferredStatusError; + + #[test] + fn local_branches_cover_statuses_payloads_and_wire_names() { + assert_eq!( + refuse_inferred_as_observed(EvidenceStatus::Inferred), + Err(InferredStatusError::InferredIsNotObserved) + ); + assert_eq!( + refuse_inferred_as_transition(EvidenceStatus::Inferred), + Err(InferredStatusError::InferredIsNotTransition) + ); + refuse_inferred_as_observed(EvidenceStatus::Observed).expect("observed"); + refuse_inferred_as_transition(EvidenceStatus::Observed).expect("observed"); + assert!(status_is_observed(EvidenceStatus::Observed).expect("observed")); + assert!(!status_is_observed(EvidenceStatus::Inferred).expect("inferred")); + for status in [EvidenceStatus::Observed, EvidenceStatus::Inferred] { + assert_eq!( + EvidenceStatus::from_wire_name(status.wire_name()).expect("round-trip"), + status + ); + } + assert_eq!( + EvidenceStatus::from_wire_name("promoted"), + Err(InferredStatusError::InvalidStatusPayload) + ); + let matched = + identity_recovery_rate(&[EvidenceStatus::Inferred], &[EvidenceStatus::Inferred]) + .expect("rate"); + assert!((matched - 1.0).abs() < f64::EPSILON); + assert_eq!( + identity_recovery_rate(&[], &[]), + Err(InferredStatusError::InvalidStatusPayload) + ); + assert_eq!( + identity_recovery_rate(&[EvidenceStatus::Inferred], &[]), + Err(InferredStatusError::InvalidStatusPayload) + ); + } +} diff --git a/crates/inferred_status/tests/crate_contract.rs b/crates/inferred_status/tests/crate_contract.rs new file mode 100644 index 000000000..f37adc447 --- /dev/null +++ b/crates/inferred_status/tests/crate_contract.rs @@ -0,0 +1,7 @@ +//! Integration contract for the `inferred_status` package identity. + +#[test] +fn package_identity_is_stable() { + let observed = std::hint::black_box(env!("CARGO_PKG_NAME")); + assert_eq!(observed, "inferred_status"); +} diff --git a/crates/inferred_status/tests/inferred_status_contract.rs b/crates/inferred_status/tests/inferred_status_contract.rs new file mode 100644 index 000000000..ade53ccf9 --- /dev/null +++ b/crates/inferred_status/tests/inferred_status_contract.rs @@ -0,0 +1,70 @@ +//! Inferred relations cannot be promoted to observed evidence or transitions. + +use inferred_status::{ + EvidenceStatus, InferredStatusError, identity_recovery_rate, refuse_inferred_as_observed, + refuse_inferred_as_transition, status_is_observed, +}; + +#[test] +fn inferred_status_cannot_become_observed_or_a_transition() { + assert_eq!( + refuse_inferred_as_observed(EvidenceStatus::Inferred), + Err(InferredStatusError::InferredIsNotObserved) + ); + assert_eq!( + refuse_inferred_as_transition(EvidenceStatus::Inferred), + Err(InferredStatusError::InferredIsNotTransition) + ); + refuse_inferred_as_observed(EvidenceStatus::Observed).expect("observed stays observed"); + refuse_inferred_as_transition(EvidenceStatus::Observed) + .expect("observed may be considered for promotion elsewhere"); + assert!(status_is_observed(EvidenceStatus::Observed).expect("observed")); + assert!(!status_is_observed(EvidenceStatus::Inferred).expect("inferred")); +} + +#[test] +fn recovered_statuses_match_known_truth_better_than_an_observed_collapse() { + let truth = [ + EvidenceStatus::Observed, + EvidenceStatus::Inferred, + EvidenceStatus::Inferred, + ]; + let recovered = truth; + let collapsed = [ + EvidenceStatus::Observed, + EvidenceStatus::Observed, + EvidenceStatus::Observed, + ]; + let recovered_rate = identity_recovery_rate(&truth, &recovered).expect("recovered"); + let collapsed_rate = identity_recovery_rate(&truth, &collapsed).expect("collapsed"); + let expected = { + let mut matches = 0_u32; + for (truth_status, decided_status) in truth.iter().zip(recovered.iter()) { + if truth_status == decided_status { + matches += 1; + } + } + f64::from(matches) / f64::from(u32::try_from(truth.len()).expect("len")) + }; + assert!((recovered_rate - expected).abs() < f64::EPSILON); + assert!(recovered_rate > collapsed_rate); +} + +#[test] +fn empty_or_mismatched_status_payloads_fail_closed() { + assert_eq!( + identity_recovery_rate(&[], &[]), + Err(InferredStatusError::InvalidStatusPayload) + ); + assert_eq!( + identity_recovery_rate(&[EvidenceStatus::Inferred], &[]), + Err(InferredStatusError::InvalidStatusPayload) + ); + assert_eq!( + identity_recovery_rate( + &[EvidenceStatus::Observed, EvidenceStatus::Inferred], + &[EvidenceStatus::Observed] + ), + Err(InferredStatusError::InvalidStatusPayload) + ); +} diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index f775cce08..98092190d 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -14,7 +14,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | 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; `support_edge` evidential-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 | -| time-varying cross-classified multiple membership | PRD; ADR 0003 | `membership_core` network on protected main; multilevel estimators remaining | partial | +| time-varying cross-classified multiple membership | PRD; ADR 0003 | `membership_core` network on protected main; `inferred_status` inferred-versus-observed identity on the active PR; multilevel estimators remaining | partial | | leakage-safe availability/cutoff snapshots | PRD; ADR 0002/0013 | `corpus_split` on protected main | implemented-main | | recovery metrics (RMSE, bias, coverage, graph, temporal order, Monte Carlo SE gates) | PRD; Test Strategy; ADR 0007/0014 | `validation_core` on protected main (PR #19); SE-aware Monte Carlo gates included | implemented-main | | PostgreSQL bitemporal/lineage persistence | ADR 0013; Architecture/ERD | `persistence_postgres` on protected main as before; `revision_order` later-revision system-time gate on the active PR; remaining physical ERD constraints | partial | diff --git a/docs/adr/0003-relational-event-multiple-membership.md b/docs/adr/0003-relational-event-multiple-membership.md index e3e55559e..e9c668a69 100644 --- a/docs/adr/0003-relational-event-multiple-membership.md +++ b/docs/adr/0003-relational-event-multiple-membership.md @@ -1,6 +1,7 @@ # ADR 0003 — Relational event ontology and time-varying multiple membership **Decision status:** Accepted +**Implementation maturity:** partial — membership network and event mention/instance separation implemented-main; inferred-versus-observed identity in `inferred_status` on the active PR; typed relation graph with forward-only transitions active-PR; multilevel estimators and persistence remain accepted-target **Implementation maturity:** partial — membership network and event mention/instance separation implemented-main; evidential-vs-transition identity in `support_edge` on the active PR; typed relation graph with forward-only transitions implemented-main; multilevel estimators and persistence remain accepted-target **Implementation maturity:** active-PR — subevent parent-window containment in `subevent_containment` on the active PR; multilevel estimators remain accepted-target **Implementation maturity:** partial — membership network, event mention/instance separation, and Kish ESS implemented-main; nested ICC with cross-classified/multiple-membership refusal is this increment; full multilevel/MMMC estimators and remaining persistence remain accepted-target diff --git a/docs/adr/README.md b/docs/adr/README.md index 91ff984fe..bcc4e3d57 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -7,6 +7,8 @@ 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 | Unmerged PR #8 is the canonical Task 3 replacement implementing typed clocks/intervals against the current protected-main lineage; conflicted PR #5 is superseded lineage. Later graph/split enforcement remains target work. | +| [0003](0003-relational-event-multiple-membership.md) | Relational event ontology and time-varying cross-classified multiple membership | Accepted | partial | Weighted time-varying membership network/roles are implemented-main (PR #12); inferred-versus-observed identity is `inferred_status` on the active PR; full multilevel estimators and persistence remain accepted-target. ADR 0016 owns event-intelligence tasks. | | [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | active-PR | Evidential-vs-transition gate in `support_edge` on the active PR; remaining graph/split enforcement stays accepted-target. | | [0003](0003-relational-event-multiple-membership.md) | Relational event ontology and time-varying cross-classified multiple membership | Accepted | partial | Evidential-vs-transition identity in `support_edge` on the active PR; membership network/roles remain implemented-main; full multilevel estimators and persistence remain accepted-target. ADR 0016 owns event-intelligence tasks. | | [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. | diff --git a/docs/research/inferred-status-identity.md b/docs/research/inferred-status-identity.md new file mode 100644 index 000000000..46a8cbf92 --- /dev/null +++ b/docs/research/inferred-status-identity.md @@ -0,0 +1,30 @@ +# Inferred status is not observed evidence (doctoring) + +## Scope + +`inferred_status` keeps model, reasoner, and heuristic proposals +inferred. They cannot be treated as observed documentary evidence or as +forward state transitions. Recovery is the computed share of recovered +statuses that match known truth. + +This slice does not persist the graph, allocate migration `0008`, or +replace `relation_graph` or `relation_absence`. + +## Authority + +### Normative TEPP contract + +- `docs/adr/0003-relational-event-multiple-membership.md` — observed + relation evidence, inferred relations, and promoted transition edges + remain distinct. Untrusted LLM output may propose mentions or + relations but cannot promote them without deterministic schema, + evidence, authorization, and scientific validation. + +### Supporting literature + +Moreau and Missier (2013) distinguish generated/derived activity from +the entity it describes. Inference is a derivation, not an observation +of the source. + +Moreau, L., & Missier, P. (Eds.). (2013). *PROV-DM: The PROV data +model*. World Wide Web Consortium. https://www.w3.org/TR/prov-dm/ diff --git a/docs/research/standards-and-literature.md b/docs/research/standards-and-literature.md index 755b06f7d..cfc58f888 100644 --- a/docs/research/standards-and-literature.md +++ b/docs/research/standards-and-literature.md @@ -125,35 +125,7 @@ Lebo, T., Sahoo, S., & McGuinness, D. (Eds.). (2013). *PROV-O: The PROV ontology Moreau, L., & Missier, P. (Eds.). (2013). *PROV-DM: The PROV data model*. World Wide Web Consortium. https://www.w3.org/TR/prov-dm/ -National Academies of Sciences, Engineering, and Medicine. (2019). *Reproducibility and replicability in science*. The National Academies Press. https://doi.org/10.17226/25303 - -Peng, R. D. (2011). Reproducible research in computational science. *Science, 334*(6060), 1226–1227. https://doi.org/10.1126/science.1213847 - -TEPP separates stable record identity, content equality, exact text location, wire representation, authorization, and provenance. JSON wire records are explicit versioned DTOs with unknown-field rejection and reconstruct through domain validation. `SHA-256` detects content substitution but is not treated as proof of origin, authority, or chain of custody. `encrypted_mapping` uses AES-256-GCM authenticated encryption with an operating-system-generated nonce and identifier-bound associated data (Dworkin, 2007); HMAC-SHA-256 remains a key-material normalization primitive (Krawczyk et al., 1997; Nystrom, 2005). A model checkpoint is a derived run artifact whose digest verifies bytes (National Institute of Standards and Technology, 2015); it does not become the CPU `f64` estimator or a scientific claim (Peng, 2011; National Academies of Sciences, Engineering, and Medicine, 2019). These constructions are not a CSAP or SOC 2 certification claim and do not persist mappings. - -## Privacy and operational log/source separation - -International Organization for Standardization and International Electrotechnical Commission. (2024). *Information technology—Security techniques—Privacy framework* (ISO/IEC Standard No. 29100:2024). https://www.iso.org/standard/85938.html Data minimization informs `operational_log`; it is not a certification claim. The 2011 edition and its 2018 amendment are withdrawn. - -International Organization for Standardization and International Electrotechnical Commission. (2022). *Information security, cybersecurity and privacy protection—Information security controls* (ISO/IEC Standard No. 27002:2022). Logging is an operational control, not authorization to copy source text or source identity into the log. - -International Organization for Standardization and International Electrotechnical Commission. (2011). *Information technology—Security techniques—Privacy framework* (ISO/IEC Standard No. 29100:2011). Data minimization informs `provider_receipt`; it is not a certification claim. - -Allen, J. F. (1983). Maintaining knowledge about temporal intervals. *Communications of the ACM, 26*(11), 832–843. https://doi.org/10.1145/182.358434. The `during` relation informs `subevent_containment`; the crate does not implement Allen composition. - -Allen, J. F. (1983). Maintaining knowledge about temporal intervals. *Communications of the ACM, 26*(11), 832–843. https://doi.org/10.1145/182.358434. Interval relations inform `citation_edge`; they do not make a citation a state transition. - -Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL*. Morgan Kaufmann. Transaction/system time informs `revision_order`; a later recorded revision cannot precede an earlier one. - -Jensen, C. S., & Snodgrass, R. T. (1996). Semantics of time-varying information. *Information Systems, 21*(4), 311–352. https://doi.org/10.1016/0306-4379(96)00017-8 Valid time versus transaction/system time informs `document_clocks`; assertion time and document time remain additional TEPP clocks and cannot be omitted or replaced by event or system time. - -Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL*. Morgan Kaufmann. Valid vs transaction time informs `available_clock`; availability is a third TEPP clock. - -Snodgrass, R. T. (2000). *Developing time-oriented database applications in SQL*. Morgan Kaufmann. Valid vs transaction time informs `assertion_clock`; assertion time is a distinct TEPP clock. - -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. +TEPP separates stable record identity, content equality, exact text location, wire representation, authorization, and provenance. JSON wire records are explicit versioned DTOs with unknown-field rejection and reconstruct through domain validation. `SHA-256` detects content substitution but is not treated as proof of origin, authority, or chain of custody. Inferred relations are a PROV derivation, not an observation, and cannot be promoted to observed evidence or to a state transition without a separate validation gate (Moreau & Missier, 2013). ## Privacy lifecycle, retention, and legal hold diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index 5c0b42065..1b5b2862c 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -20,6 +20,7 @@ This report tracks exact-head scientific and engineering evidence required befor | Event mention/instance | `event_core` | partial | — | unit + fail-closed promotion | Task 5 / PR #13 | | Multiple membership | `membership_core` | partial | nested ICC + non-nested refusal | unit + ESS + nested ICC recovery | Task 7 / PR #12 + #25 + this increment | | Forward transition DAG | `relation_graph` | implemented-main | — | unit + cycle rejection | Task 6 / PR #14 | +| Inferred-versus-observed promotion | `inferred_status` | accepted-target | active PR | refuse inferred-as-observed/transition + recovery vs observed collapse | ADR 0003 | | Evidential-vs-transition gate | `support_edge` | active-PR | this PR | recovered kind rate vs support collapse | ADR 0002/0003 | | Bitemporal persistence + live SQL port | `persistence_postgres` | partial | backup/restore integrity | migration contracts + recording transport + optional PgPool + live CI + tenant RLS + `0005`/`0006` + event relation/mention/instance + source-artifact + audit-event + concurrent-write (#37–#43 implemented-main) + restore integrity probes (active PR) | Task 8 / PR #16 + #23 + #26 + #27 + #29 + #30–#43 + restore integrity | | Bitemporal persistence + live SQL port | `persistence_postgres` | partial | backup/restore integrity | migration contracts + recording transport + optional PgPool + live CI + tenant RLS + `0005`/`0006` + event relation/mention/instance + source-artifact + audit-event + concurrent-write (#37–#43 implemented-main) + restore integrity probes (#44 implemented-main) | Task 8 / PR #16 + #23 + #26 + #27 + #29 + #30–#44 + restore integrity | diff --git a/scripts/check_workspace_contract.py b/scripts/check_workspace_contract.py index 83898f30b..c21b96b33 100644 --- a/scripts/check_workspace_contract.py +++ b/scripts/check_workspace_contract.py @@ -23,6 +23,7 @@ "tepp_simulation", "validation_core", "tepp_api", + "inferred_status", "support_edge", "system_clock", "event_clock",