diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ffe514db6..9ce88e869 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -119,6 +119,8 @@ TEPP stores event/valid time, assertion time, document time, system time, availa \operatorname{available\_time}(d) \leq \operatorname{knowledge\_cutoff}. \] +When availability is an interval, every possible instant in that interval must satisfy the inequality. Unknown or open-ended availability that can extend past the cutoff fails closed; event time and document time cannot substitute for availability. + Forward transition edges require a temporally valid partial order. Retrospective, revision, translation, citation, support, and contradiction relations retain their direction and provenance but do not create reverse state transitions. ## Measurement invariants diff --git a/CHANGELOG.md b/CHANGELOG.md index 36c2e8dd9..1ee0b676b 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 +- `temporal_core` interval-aware historical eligibility: `evaluate_historical_eligibility` admits an `AvailableTime` interval only when every possible availability instant is at or before `KnowledgeCutoff`; unknown and open-ended upper availability fail closed, and event/document time cannot be substituted. - `tepp_api` naruon live loopback HTTP/1.1 listener: `serve_one` installs a read/write deadline, requires a loopback `Host`, refuses `Transfer-Encoding` and NIM/proxy credential headers, parses `knowledge_cutoff` as RFC 3339 and refuses a future cutoff, keys analysis-run idempotency by tenant plus key, and proves both analysis-run and export POSTs over a real `TcpStream`. Not a production TLS/`$PORT` service (ADR 0011). - `tepp_api` adaptive orchestration router (ADR 0010): versioned `direct`/`verify`/`committee`/`conductor`/`abstain` selection from CPU `f64` risk, ambiguity, evidence, and token-budget inputs; recorded stages, recursion, decomposition, access lists, and role-specific reasoning effort; fail-closed document-controlled policy/access/credentials; LLM plans remain proposals under deterministic statistical authority; comparable-budget ablation requires a direct baseline; credential-free contextual-orchestrator binding. Live NIM HTTP remains accepted-target. - `tepp_api` purpose-bound provider-payload minimization: time-bounded `PurposeGrant` evaluation, fail-closed expired/not-yet-valid/inverted/cross-tenant/impossible-calendar denial, semantic UTC calendar validation, refusal to copy identity mappings into model-provider payloads or ordinary logs, preservation of opaque analytical identifiers and membership roles (no blanket PII mask), a separately authorized scientific re-identification path, and an internally bound FIPS 180-4 SHA-256 audit digest appended through `ReidentificationAuditSink` before disclosure. diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 3f0949473..d7d98958b 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -27,6 +27,7 @@ TEPP's approved PRD v0.4 and implementation plan are the primary product baselin | Foundation implementation plan | [`docs/superpowers/plans/2026-08-05-temporal-event-foundation.md`](docs/superpowers/plans/2026-08-05-temporal-event-foundation.md) | | Foundation validation ledger | [`docs/validation/temporal-event-foundation.md`](docs/validation/temporal-event-foundation.md) | | Standards and APA 7 literature | [`docs/research/standards-and-literature.md`](docs/research/standards-and-literature.md) | +| Interval cutoff eligibility doctoring | [`docs/research/interval-cutoff-eligibility.md`](docs/research/interval-cutoff-eligibility.md) | | Governance | [`GOVERNANCE.md`](GOVERNANCE.md) | | Agent development rules | [`AGENTS.md`](AGENTS.md) | | Agent context | [`CLAUDE.md`](CLAUDE.md) | diff --git a/crates/temporal_core/src/eligibility.rs b/crates/temporal_core/src/eligibility.rs new file mode 100644 index 000000000..131741704 --- /dev/null +++ b/crates/temporal_core/src/eligibility.rs @@ -0,0 +1,79 @@ +//! Interval-aware historical eligibility against a knowledge cutoff. + +use crate::{ + AvailableTime, KnowledgeCutoff, TemporalBoundary, TemporalCertainty, TemporalError, + TemporalInterval, +}; + +/// Decide whether an availability interval is fully eligible under `knowledge_cutoff`. +/// +/// Evidence may enter a historical analysis only when every possible +/// availability instant is at or before the cutoff. Unknown availability and +/// open-ended upper bounds fail closed because they can extend past the cutoff. +/// Event time and document time cannot be substituted: the interval is typed as +/// [`AvailableTime`]. +/// +/// ```compile_fail,E0308 +/// use temporal_core::{ +/// EventTime, KnowledgeCutoff, TemporalInterval, TemporalPrecision, +/// evaluate_historical_eligibility, +/// }; +/// +/// let event = TemporalInterval::exact( +/// EventTime::parse_rfc3339("2026-01-01T00:00:00Z")?, +/// TemporalPrecision::Second, +/// )?; +/// let cutoff = KnowledgeCutoff::parse_rfc3339("2026-06-01T00:00:00Z")?; +/// evaluate_historical_eligibility(&event, &cutoff)?; +/// # Ok::<(), temporal_core::TemporalError>(()) +/// ``` +/// +/// # Errors +/// +/// Returns [`TemporalError::UncertainAvailability`] when the interval cannot +/// prove an upper bound, or [`TemporalError::IneligibleAtCutoff`] when the +/// latest possible availability is after the cutoff. +pub fn evaluate_historical_eligibility( + availability: &TemporalInterval, + knowledge_cutoff: &KnowledgeCutoff, +) -> Result<(), TemporalError> { + if matches!(availability.certainty(), TemporalCertainty::Unknown) { + return Err(TemporalError::UncertainAvailability); + } + + let latest = match availability.upper() { + TemporalBoundary::Unbounded => return Err(TemporalError::UncertainAvailability), + TemporalBoundary::Included(value) => value.instant().as_nanosecond(), + TemporalBoundary::Excluded(value) => value.instant().as_nanosecond() - 1, + }; + if latest <= knowledge_cutoff.instant().as_nanosecond() { + Ok(()) + } else { + Err(TemporalError::IneligibleAtCutoff) + } +} + +#[cfg(test)] +mod tests { + use super::evaluate_historical_eligibility; + use crate::{ + AvailableTime, KnowledgeCutoff, TemporalBoundary, TemporalInterval, TemporalPrecision, + }; + + fn available(stamp: &str) -> AvailableTime { + AvailableTime::parse_rfc3339(stamp).expect("available") + } + + #[test] + fn excluded_upper_one_nanosecond_after_cutoff_is_eligible() { + let cutoff = KnowledgeCutoff::parse_rfc3339("2026-06-01T00:00:00Z").expect("cutoff"); + let just_after = available("2026-06-01T00:00:00.000000001Z"); + let interval = TemporalInterval::bounded( + TemporalBoundary::Unbounded, + TemporalBoundary::Excluded(just_after), + TemporalPrecision::Nanosecond, + ) + .expect("interval"); + assert_eq!(evaluate_historical_eligibility(&interval, &cutoff), Ok(())); + } +} diff --git a/crates/temporal_core/src/error.rs b/crates/temporal_core/src/error.rs index d97eb8fd4..d3778bde2 100644 --- a/crates/temporal_core/src/error.rs +++ b/crates/temporal_core/src/error.rs @@ -24,6 +24,10 @@ pub enum TemporalError { UnsupportedWireVersion, /// A JSON wire record declared a different nominal clock type. ClockTypeMismatch, + /// Availability is unknown or open-ended and can extend past the cutoff. + UncertainAvailability, + /// The latest possible availability instant is after the knowledge cutoff. + IneligibleAtCutoff, } impl fmt::Display for TemporalError { @@ -40,6 +44,8 @@ impl fmt::Display for TemporalError { Self::InvalidWirePayload => "invalid temporal wire payload", Self::UnsupportedWireVersion => "unsupported temporal wire version", Self::ClockTypeMismatch => "temporal clock type mismatch", + Self::UncertainAvailability => "uncertain availability fails closed at cutoff", + Self::IneligibleAtCutoff => "availability is ineligible at knowledge cutoff", }; formatter.write_str(message) } diff --git a/crates/temporal_core/src/lib.rs b/crates/temporal_core/src/lib.rs index ede257114..8980da4cb 100644 --- a/crates/temporal_core/src/lib.rs +++ b/crates/temporal_core/src/lib.rs @@ -25,8 +25,13 @@ //! relations. Relation sets support inverse and complete composition, while a //! resource-bounded path-consistency reasoner preserves direct assertions, //! derived narrowing, and conservative supporting-assertion provenance. +//! +//! Historical eligibility requires the entire [`AvailableTime`] interval to +//! fall at or before [`KnowledgeCutoff`]. Unknown or open-ended availability +//! fails closed and cannot be replaced by event or document time. mod clock; +mod eligibility; mod error; mod instant; mod interval; @@ -48,6 +53,8 @@ pub use clock::KnowledgeCutoff; pub use clock::SystemTime; /// A sealed nominal TEPP clock over one absolute instant representation. pub use clock::TemporalClock; +/// Decide whether an availability interval is fully eligible at a cutoff. +pub use eligibility::evaluate_historical_eligibility; /// A fail-closed temporal-domain validation error. pub use error::TemporalError; /// An absolute UTC instant represented to nanosecond precision. diff --git a/crates/temporal_core/tests/eligibility_contract.rs b/crates/temporal_core/tests/eligibility_contract.rs new file mode 100644 index 000000000..261599b87 --- /dev/null +++ b/crates/temporal_core/tests/eligibility_contract.rs @@ -0,0 +1,128 @@ +//! Interval-aware historical eligibility against a knowledge cutoff. + +use temporal_core::{ + AvailableTime, KnowledgeCutoff, TemporalBoundary, TemporalError, TemporalInterval, + TemporalPrecision, evaluate_historical_eligibility, +}; + +fn available(stamp: &str) -> AvailableTime { + AvailableTime::parse_rfc3339(stamp).expect("available") +} + +fn cutoff(stamp: &str) -> KnowledgeCutoff { + KnowledgeCutoff::parse_rfc3339(stamp).expect("cutoff") +} + +fn exact(stamp: &str) -> TemporalInterval { + TemporalInterval::exact(available(stamp), TemporalPrecision::Second).expect("exact") +} + +/// Independently compute the latest representable availability nanosecond. +/// +/// The production gate must agree with this comparison: eligible iff the +/// latest possible availability instant is `<=` the cutoff. Unknown or +/// open-ended availability has no latest instant and must fail closed. +fn latest_possible_ns( + availability: &TemporalInterval, +) -> Result { + if !availability.is_known() { + return Err(TemporalError::UncertainAvailability); + } + match availability.upper() { + TemporalBoundary::Unbounded => Err(TemporalError::UncertainAvailability), + TemporalBoundary::Included(value) => Ok(value.instant().as_nanosecond()), + TemporalBoundary::Excluded(value) => Ok(value.instant().as_nanosecond() - 1), + } +} + +fn expected_decision( + availability: &TemporalInterval, + knowledge_cutoff: &KnowledgeCutoff, +) -> Result<(), TemporalError> { + match latest_possible_ns(availability) { + Ok(latest) if latest <= knowledge_cutoff.instant().as_nanosecond() => Ok(()), + Ok(_) => Err(TemporalError::IneligibleAtCutoff), + Err(error) => Err(error), + } +} + +#[test] +fn computed_latest_instant_agrees_with_the_eligibility_gate() { + let cut = cutoff("2026-06-01T00:00:00Z"); + let closed = |start: &str, end: &str| { + TemporalInterval::bounded( + TemporalBoundary::Included(available(start)), + TemporalBoundary::Included(available(end)), + TemporalPrecision::Second, + ) + .expect("closed") + }; + let upper_open = |end: &str| { + TemporalInterval::bounded( + TemporalBoundary::Unbounded, + TemporalBoundary::Excluded(available(end)), + TemporalPrecision::Second, + ) + .expect("upper open") + }; + let lower_open = |start: &str| { + TemporalInterval::bounded( + TemporalBoundary::Included(available(start)), + TemporalBoundary::Unbounded, + TemporalPrecision::Second, + ) + .expect("lower open") + }; + + let cases = [ + exact("2026-06-01T00:00:00Z"), + exact("2026-05-01T00:00:00Z"), + exact("2026-06-01T00:00:01Z"), + closed("2026-01-01T00:00:00Z", "2026-06-01T00:00:00Z"), + closed("2026-01-01T00:00:00Z", "2026-06-01T00:00:01Z"), + upper_open("2026-06-01T00:00:00Z"), + upper_open("2026-06-01T00:00:00.000000001Z"), + upper_open("2026-06-01T00:00:00.000000002Z"), + lower_open("2026-01-01T00:00:00Z"), + TemporalInterval::::unknown(), + ]; + + for availability in cases { + assert_eq!( + evaluate_historical_eligibility(&availability, &cut), + expected_decision(&availability, &cut) + ); + } +} + +#[test] +fn unknown_and_open_ended_availability_fail_closed() { + let cut = cutoff("2026-06-01T00:00:00Z"); + assert_eq!( + evaluate_historical_eligibility(&TemporalInterval::unknown(), &cut), + Err(TemporalError::UncertainAvailability) + ); + let open_upper = TemporalInterval::bounded( + TemporalBoundary::Included(available("2026-01-01T00:00:00Z")), + TemporalBoundary::Unbounded, + TemporalPrecision::Day, + ) + .expect("open upper"); + assert_eq!( + evaluate_historical_eligibility(&open_upper, &cut), + Err(TemporalError::UncertainAvailability) + ); +} + +#[test] +fn exact_availability_after_cutoff_is_ineligible() { + let cut = cutoff("2026-06-01T00:00:00Z"); + assert_eq!( + evaluate_historical_eligibility(&exact("2026-06-01T00:00:00Z"), &cut), + Ok(()) + ); + assert_eq!( + evaluate_historical_eligibility(&exact("2026-06-01T00:00:01Z"), &cut), + Err(TemporalError::IneligibleAtCutoff) + ); +} diff --git a/crates/temporal_core/tests/error_contract.rs b/crates/temporal_core/tests/error_contract.rs index 25b3d316b..838cbb440 100644 --- a/crates/temporal_core/tests/error_contract.rs +++ b/crates/temporal_core/tests/error_contract.rs @@ -38,6 +38,14 @@ fn every_temporal_error_has_a_stable_content_redacting_message() { TemporalError::ClockTypeMismatch, "temporal clock type mismatch", ), + ( + TemporalError::UncertainAvailability, + "uncertain availability fails closed at cutoff", + ), + ( + TemporalError::IneligibleAtCutoff, + "availability is ineligible at knowledge cutoff", + ), ]; for (error, expected) in cases { diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index a3e674cfb..8e842c549 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -16,6 +16,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | 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 | | leakage-safe availability/cutoff snapshots | PRD; ADR 0002/0013 | `corpus_split` on protected main | implemented-main | +| interval-aware historical eligibility (`available_time` fully ≤ cutoff) | ADR 0002 | `temporal_core` `evaluate_historical_eligibility` on the active PR; unknown/open-ended availability fails closed | active-PR | | 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` migration contracts, in-memory adapters, live SQL session/document SQL port, tenant RLS (`0002` + session GUC/role helpers), `DATABASE_URL` SQLx gate, optional `live-sqlx` `PgPool` driver, exact-head live PostgreSQL CI with isolation proof, append-only immutability triggers (`0004`), temporal interval ordering CHECKs (`0005`), typed membership assignment (`0006` implemented-main), event-relation/mention/instance SQL (#37–#39 implemented-main), source-artifact SQL (#40 implemented-main), audit-event SQL (#41 implemented-main), concurrent document-write stress (#43 implemented-main), backup/restore integrity revalidation (active PR); remaining physical ERD constraints | partial | | known-truth temporal/event simulation manifests | PRD; TRD; Test Strategy | `tepp_simulation` on protected main; recovery metrics in `validation_core` | implemented-main | diff --git a/docs/adr/0002-six-clock-temporal-semantics.md b/docs/adr/0002-six-clock-temporal-semantics.md index c06f7d380..37a12e68e 100644 --- a/docs/adr/0002-six-clock-temporal-semantics.md +++ b/docs/adr/0002-six-clock-temporal-semantics.md @@ -1,7 +1,7 @@ # ADR 0002 — Six-clock temporal semantics and leakage prevention **Decision status:** Accepted -**Implementation maturity:** active-PR — unmerged PR #8 is the canonical replacement implementing typed clocks/intervals against the current protected-main lineage; superseded/conflicted PR #5 is historical lineage only; downstream transition/split enforcement remains accepted-target +**Implementation maturity:** partial — typed clocks/intervals and Allen path-consistency are implemented-main (PR #8/#9); interval-aware historical eligibility (`AvailableTime` interval fully ≤ `KnowledgeCutoff`, unknown/open-ended availability fail closed) is active-PR; remaining downstream split/persistence enforcement remains accepted-target **Date:** 2026-08-05 **Supersedes:** None. ADR 0013 owns persistence/split representation; ADR 0016 owns event-intelligence reasoning above these temporal primitives. diff --git a/docs/adr/0009-purpose-bound-pii-governance.md b/docs/adr/0009-purpose-bound-pii-governance.md index 88ed13413..a4fa99808 100644 --- a/docs/adr/0009-purpose-bound-pii-governance.md +++ b/docs/adr/0009-purpose-bound-pii-governance.md @@ -1,9 +1,9 @@ # ADR 0009 — Purpose-bound PII governance without blanket masking -**Decision status:** Accepted +**Decision status:** Accepted **Implementation maturity:** partial — persistence retention/deletion/legal-hold (migration `0007`) is implemented-main; purpose-bound provider-payload minimization (expired-purpose denial, log/source separation, separately authorized re-identification) is on the active PR and is not implemented-main until exact-head checks, review, and protected-main integration complete; deployment/provider-region evidence remains accepted-target -**Date:** 2026-08-10 +**Date:** 2026-08-10 **Supersedes:** None. ## Context diff --git a/docs/adr/0010-adaptive-llm-orchestration.md b/docs/adr/0010-adaptive-llm-orchestration.md index a33789839..093617dfd 100644 --- a/docs/adr/0010-adaptive-llm-orchestration.md +++ b/docs/adr/0010-adaptive-llm-orchestration.md @@ -1,8 +1,8 @@ # ADR 0010 — Adaptive LLM orchestration and test-time compute -**Decision status:** Accepted +**Decision status:** Accepted **Implementation maturity:** partial — `tepp_api` governed router, comparable-budget ablation record, and credential-free contextual-orchestrator binding are implemented on the active PR and are not implemented-main until exact-head checks, review, and protected-main integration complete; live NIM execution, learned conductor calibration, and production ablation evidence remain accepted-target -**Date:** 2026-08-10 +**Date:** 2026-08-10 **Supersedes:** The LLM orchestration-selection/ablation clauses previously co-located in ADR 0006. ADR 0006 remains authoritative for GPU/VRAM and model-credential separation; ADR 0015 governs autonomous repository-write/review/merge authority. ## Context diff --git a/docs/adr/0011-standalone-modular-msa-boundary.md b/docs/adr/0011-standalone-modular-msa-boundary.md index d545ee23f..5e16e4a4e 100644 --- a/docs/adr/0011-standalone-modular-msa-boundary.md +++ b/docs/adr/0011-standalone-modular-msa-boundary.md @@ -1,8 +1,8 @@ # ADR 0011 — Standalone operation and modular CWL MSA boundary -**Decision status:** Accepted -**Implementation maturity:** partial — Rust crates are independently usable; naruon HTTP interchange and loopback live listener (`POST /v1/analysis-runs` and `/v1/exports`, fail-closed table-access, NIM/proxy headers, RFC 3339 cutoff, stream deadline) are on the active PR (not implemented-main); production TLS/`$PORT` and remaining persistence integrations remain accepted-target -**Date:** 2026-08-10 +**Decision status:** Accepted +**Implementation maturity:** partial — Rust crates are independently usable; naruon HTTP interchange and loopback live listener (`POST /v1/analysis-runs` and `/v1/exports`, fail-closed table-access, NIM/proxy headers, RFC 3339 cutoff, stream deadline) are on the active PR (not implemented-main); production TLS/`$PORT` and remaining persistence integrations remain accepted-target +**Date:** 2026-08-10 **Supersedes:** The broad cross-service ownership wording in ADR 0001. ADR 0001 remains authoritative for Rust-first numerical architecture. ## Context diff --git a/docs/adr/0013-bitemporal-persistence-reproducibility-and-split-authority.md b/docs/adr/0013-bitemporal-persistence-reproducibility-and-split-authority.md index a593ddb16..71a56b8d5 100644 --- a/docs/adr/0013-bitemporal-persistence-reproducibility-and-split-authority.md +++ b/docs/adr/0013-bitemporal-persistence-reproducibility-and-split-authority.md @@ -1,8 +1,8 @@ # ADR 0013 — Bitemporal persistence, reproducibility manifests, and split authority -**Decision status:** Accepted -**Implementation maturity:** partial — migration contracts, cutoff eligibility, in-memory bitemporal adapters, live SQL session/migration port, document SQL contracts, `DATABASE_URL` SQLx gate, optional `live-sqlx` `PgPool` open/execute driver, exact-head live PostgreSQL CI, tenant RLS (`tepp_app_runtime` + session GUC), append-only reproducibility-manifest SQL insert/lookup, model-run / model-artifact / corpus-split-manifest chain (migration `0003`), append-only immutability triggers (migration `0004`), temporal interval ordering CHECK constraints (migration `0005`), typed membership-assignment storage (migration `0006`), event-relation/mention/instance SQL, source-artifact SQL, audit-event action-code validation, and concurrent document-write stress implemented-main; backup/restore integrity revalidation on the active PR -**Date:** 2026-08-12 +**Decision status:** Accepted +**Implementation maturity:** partial — migration contracts, cutoff eligibility, in-memory bitemporal adapters, live SQL session/migration port, document SQL contracts, `DATABASE_URL` SQLx gate, optional `live-sqlx` `PgPool` open/execute driver, exact-head live PostgreSQL CI, tenant RLS (`tepp_app_runtime` + session GUC), append-only reproducibility-manifest SQL insert/lookup, model-run / model-artifact / corpus-split-manifest chain (migration `0003`), append-only immutability triggers (migration `0004`), temporal interval ordering CHECK constraints (migration `0005`), typed membership-assignment storage (migration `0006`), event-relation/mention/instance SQL, source-artifact SQL, audit-event action-code validation, and concurrent document-write stress implemented-main; backup/restore integrity revalidation on the active PR +**Date:** 2026-08-12 **Supersedes:** None; complements ADR 0002 (temporal semantics), ADR 0008 (evidence identity), and ADR 0011 (service ownership). ## Context diff --git a/docs/adr/README.md b/docs/adr/README.md index 258eb7f31..8877e0d49 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -7,7 +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 | 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. | +| [0002](0002-six-clock-temporal-semantics.md) | Six-clock temporal semantics and fail-closed historical leakage prevention | Accepted | partial | Typed clocks/intervals and Allen reasoner are implemented-main (PR #8/#9). Interval-aware historical eligibility is active-PR. Remaining graph/split/persistence 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 active-PR (PR #12); full multilevel estimators, graph ontology, and persistence remain accepted-target. ADR 0016 owns event-intelligence tasks. | | [0004](0004-shared-multilingual-latent-space.md) | One shared multilingual latent space with explicit invariance status | Accepted | accepted-target | ADR 0012 owns the full topic-estimator/backend/global-topic contract. | | [0005](0005-posterior-esem-dsem.md) | Posterior-aware ESEM/DSEM and valid compositional coordinates | Accepted | accepted-target | Downstream psychometric authority; upstream topic/network model is clarified by ADR 0012. | diff --git a/docs/connectors/naruon-artifact-consumer.md b/docs/connectors/naruon-artifact-consumer.md index 2e4f4d6c0..5fe0424c4 100644 --- a/docs/connectors/naruon-artifact-consumer.md +++ b/docs/connectors/naruon-artifact-consumer.md @@ -1,6 +1,6 @@ # naruon modular consumer contract for TEPP artifacts -**Status:** Partial — versioned DTO, HTTP interchange, and loopback live listener on the active PR; production TLS/`$PORT` remaining +**Status:** Partial — versioned DTO, HTTP interchange, and loopback live listener on the active PR; production TLS/`$PORT` remaining **Last reviewed:** 2026-08-16 ## Boundary diff --git a/docs/research/interval-cutoff-eligibility.md b/docs/research/interval-cutoff-eligibility.md new file mode 100644 index 000000000..7a59159a6 --- /dev/null +++ b/docs/research/interval-cutoff-eligibility.md @@ -0,0 +1,33 @@ +# Interval-aware historical eligibility (doctoring) + +## Scope + +This note doctors the `temporal_core` historical-eligibility contract: + +1. evidence may enter a historical analysis only when its governed availability interval is fully at or before the knowledge cutoff; +2. unknown availability and open-ended upper bounds fail closed because they can extend past the cutoff; +3. event time and document time cannot be substituted for availability. + +Point-instant `available_time <= knowledge_cutoff` remains the exact special case. This crate owns the interval decision; persistence adapters and corpus snapshots continue to apply the same inequality to stored instants. The change allocates no database migration. + +## Authoritative sources + +Tashman, L. J. (2000). Out-of-sample tests of forecasting accuracy: An analysis and review. *International Journal of Forecasting, 16*(4), 437–450. https://doi.org/10.1016/S0169-2070(00)00065-0 + +Jensen, C. S., & Snodgrass, R. T. (1999). Temporal data management. *IEEE Transactions on Knowledge and Data Engineering, 11*(1), 36–44. https://doi.org/10.1109/69.755613 + +Allen, J. F. (1983). Maintaining knowledge about temporal intervals. *Communications of the ACM, 26*(11), 832–843. https://doi.org/10.1145/182.358434 + +## Application + +Tashman (2000) requires that evaluation origins use only information available at the origin. Jensen and Snodgrass (1999) separate valid time from transaction/availability time so a later report about an earlier event cannot leak into an earlier analysis. When availability is an interval rather than a point, Allen (1983) interval bounds are the representation: if any possible availability instant is after the cutoff, the evidence is not fully eligible. + +TEPP therefore computes the latest representable availability instant and admits the interval only when that instant is `<= knowledge_cutoff`. An unknown interval or an unbounded upper bound has no such instant and fails closed. + +## Verification + +- exact availability on or before the cutoff is eligible; one second later is not; +- a closed interval whose included upper bound is after the cutoff is ineligible; +- an exclusive upper bound one nanosecond after the cutoff remains eligible because the latest representable instant is the cutoff; +- unknown and open-ended-upper availability return `UncertainAvailability`; +- the gate agrees with an independently computed latest-instant comparison on a fixture suite. diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index aae1a06e7..99e36cb5e 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -14,6 +14,7 @@ This report tracks exact-head scientific and engineering evidence required befor |---|---|---|---|---|---| | Immutable evidence + spans | `evidence_core` | implemented-main | — | unit + wire + coverage | Task 2 | | Six-clock temporal | `temporal_core` | implemented-main | — | unit + wire | Task 3 / PR #8 | +| Interval-aware cutoff eligibility | `temporal_core` | active-PR | this PR | unknown/open-ended fail-closed + computed latest-instant agreement | ADR 0002 | | Allen path-consistency | `temporal_core` | implemented-main | — | unit + budget tests | Task 4 / PR #9 | | Event mention/instance | `event_core` | partial | — | unit + fail-closed promotion | Task 5 / PR #13 | | Multiple membership | `membership_core` | partial | — | unit + ESS weights | Task 7 / PR #12 + #25 |