From 3fced5835fd773b37abff758316c0ed41a90d956 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 19:29:45 +0900 Subject: [PATCH 01/19] feat(event): refuse TDT/CHRONOS outputs as state transitions ADR 0016 first slice: evidence-layer admission gates and first-story miss/false-alarm rates on a known story stream. No new crate or migration while 0007 remains on #45. --- CHANGELOG.md | 1 + DOCUMENTATION.md | 1 + crates/event_core/src/error.rs | 14 ++ crates/event_core/src/intelligence.rs | 204 ++++++++++++++++++ crates/event_core/src/lib.rs | 17 +- .../tests/intelligence_status_contract.rs | 81 +++++++ docs/TRACEABILITY.md | 4 +- ...tdt-chronos-event-intelligence-boundary.md | 2 +- docs/adr/README.md | 2 +- .../event-intelligence-status-gates.md | 30 +++ docs/validation/temporal-event-foundation.md | 1 + 11 files changed, 352 insertions(+), 5 deletions(-) create mode 100644 crates/event_core/src/intelligence.rs create mode 100644 crates/event_core/tests/intelligence_status_contract.rs create mode 100644 docs/research/event-intelligence-status-gates.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 9abfea7e..7fc3bc16 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 +- `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; first-story detection scores miss/false-alarm rates against a known story stream (Allan 2002 task). - `persistence_postgres` typed membership assignment (migration `0006`): `entity_record`, `project_record`, and `text_segment` plus exactly-one observed-unit and target constraints that replace the polymorphic `membership_target_id` stub, with SQL insert/lookup, fail-closed inverted-window and backslash-label refusal, and live proof that one document persists two entity memberships and one project membership. - Actions workflow fleet auditor (`scripts/actions_workflow_fleet.py`): paginated registry inventory bound to the exact default-branch SHA/tree, classification of present/orphan/disabled/GitHub-dynamic identities, and fail-closed orphan disable that confirms GitHub's official `disabled_manually` state. - `persistence_postgres` temporal interval ordering migration (`0005`): multi-word CHECK constraints on `document_record`, `event_instance`, and `membership_assignment` that reject inverted valid/system windows and non-positive document revisions while preserving open-ended NULL upper bounds and equal point bounds; catalog validation and live inverted-window proof. diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 230c5abe..22adac68 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -33,6 +33,7 @@ TEPP's approved PRD v0.4 and implementation plan are the primary product baselin | Hourly NIM product-development operations | [`docs/operations/HOURLY_NIM_PRODUCT_DEVELOPMENT.md`](docs/operations/HOURLY_NIM_PRODUCT_DEVELOPMENT.md) | | Actions workflow fleet audit | [`docs/operations/ACTIONS_WORKFLOW_FLEET.md`](docs/operations/ACTIONS_WORKFLOW_FLEET.md) | | Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | +| Event-intelligence status-gate doctoring | [`docs/research/event-intelligence-status-gates.md`](docs/research/event-intelligence-status-gates.md) | | Hourly NIM OpenCode doctoring | [`docs/doctoring/hourly-nim-opencode-development.md`](docs/doctoring/hourly-nim-opencode-development.md) | | Change history | [`CHANGELOG.md`](CHANGELOG.md) | diff --git a/crates/event_core/src/error.rs b/crates/event_core/src/error.rs index 6c795fef..f1ce2533 100644 --- a/crates/event_core/src/error.rs +++ b/crates/event_core/src/error.rs @@ -20,6 +20,10 @@ pub enum EventError { UnsupportedWireVersion, /// An unknown event-role name was supplied. UnknownEventRole, + /// A TDT detection or mention was treated as a state transition. + DetectionIsNotTransition, + /// A CHRONOS prediction was treated as an observed or promoted fact. + PredictionIsNotFact, } impl fmt::Display for EventError { @@ -32,6 +36,8 @@ impl fmt::Display for EventError { Self::InvalidWirePayload => "invalid event wire payload", Self::UnsupportedWireVersion => "unsupported event wire version", Self::UnknownEventRole => "unknown event role", + Self::DetectionIsNotTransition => "detection is not a state transition", + Self::PredictionIsNotFact => "prediction is not an observed fact", }; formatter.write_str(message) } @@ -65,6 +71,14 @@ mod tests { "unsupported event wire version", ), (EventError::UnknownEventRole, "unknown event role"), + ( + EventError::DetectionIsNotTransition, + "detection is not a state transition", + ), + ( + EventError::PredictionIsNotFact, + "prediction is not an observed fact", + ), ] { assert_eq!(error.to_string(), message); } diff --git a/crates/event_core/src/intelligence.rs b/crates/event_core/src/intelligence.rs new file mode 100644 index 00000000..04f901ff --- /dev/null +++ b/crates/event_core/src/intelligence.rs @@ -0,0 +1,204 @@ +//! Evidence-status gates for TDT detection and CHRONOS prediction. + +use crate::EventError; + +/// Epistemic layer of an event-intelligence output. +/// +/// Only [`EventEvidenceLayer::PromotedTransition`] may enter the forward +/// state/input-process-outcome graph. TDT detections and CHRONOS predictions +/// remain measurement or hypothesis artifacts. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum EventEvidenceLayer { + /// Fallible textual mention grounded in evidence. + ObservedMention, + /// TDT-style detection, link, or track output. + TdtDetection, + /// CHRONOS-style schema completion or predicted event. + ChronosPrediction, + /// Symbolic temporal-consistency judgment. + TemporalConsistency, + /// Independently promoted forward state transition. + PromotedTransition, +} + +impl EventEvidenceLayer { + /// Stable wire name for this layer. + #[must_use] + pub const fn wire_name(self) -> &'static str { + match self { + Self::ObservedMention => "observed_mention", + Self::TdtDetection => "tdt_detection", + Self::ChronosPrediction => "chronos_prediction", + Self::TemporalConsistency => "temporal_consistency", + Self::PromotedTransition => "promoted_transition", + } + } + + /// Whether this layer may admit a forward state-transition edge. + #[must_use] + pub const fn may_admit_state_transition(self) -> bool { + matches!(self, Self::PromotedTransition) + } +} + +/// Admit a layer into the forward state graph or fail closed. +/// +/// # Errors +/// +/// Returns [`EventError::PredictionIsNotFact`] for CHRONOS predictions and +/// [`EventError::DetectionIsNotTransition`] for every other non-promoted layer. +pub fn admit_state_transition(layer: EventEvidenceLayer) -> Result<(), EventError> { + if layer.may_admit_state_transition() { + Ok(()) + } else if matches!(layer, EventEvidenceLayer::ChronosPrediction) { + Err(EventError::PredictionIsNotFact) + } else { + Err(EventError::DetectionIsNotTransition) + } +} + +/// First-story versus subsequent-track decision for one candidate story. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TdtStoryDecision { + /// The story identity has not been seen in the stream. + FirstStory, + /// The story identity continues a previously seen event. + Track, +} + +/// Classify one candidate against previously seen story identities. +#[must_use] +pub fn classify_tdt_story(seen_story_ids: &[u64], candidate_story_id: u64) -> TdtStoryDecision { + if seen_story_ids.contains(&candidate_story_id) { + TdtStoryDecision::Track + } else { + TdtStoryDecision::FirstStory + } +} + +/// Known-truth first-story detection counts. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct FirstStoryRates { + hits: usize, + misses: usize, + false_alarms: usize, + first_story_truth: usize, + continuation_truth: usize, +} + +impl FirstStoryRates { + /// Correct first-story detections. + #[must_use] + pub const fn hits(self) -> usize { + self.hits + } + + /// Missed first stories. + #[must_use] + pub const fn misses(self) -> usize { + self.misses + } + + /// Continuations labeled as first stories. + #[must_use] + pub const fn false_alarms(self) -> usize { + self.false_alarms + } + + /// Miss rate among true first stories. + #[must_use] + pub fn miss_rate(self) -> f64 { + if self.first_story_truth == 0 { + 0.0 + } else { + #[allow(clippy::cast_precision_loss)] + { + self.misses as f64 / self.first_story_truth as f64 + } + } + } + + /// False-alarm rate among true continuations. + #[must_use] + pub fn false_alarm_rate(self) -> f64 { + if self.continuation_truth == 0 { + 0.0 + } else { + #[allow(clippy::cast_precision_loss)] + { + self.false_alarms as f64 / self.continuation_truth as f64 + } + } + } +} + +/// Score a first-story detector against a known binary stream. +/// +/// # Errors +/// +/// Returns [`EventError::InvalidWirePayload`] when the streams are empty or +/// have unequal length. +pub fn first_story_detection_rates( + truth_is_first: &[bool], + predicted_is_first: &[bool], +) -> Result { + if truth_is_first.is_empty() || truth_is_first.len() != predicted_is_first.len() { + return Err(EventError::InvalidWirePayload); + } + let mut hits = 0; + let mut misses = 0; + let mut false_alarms = 0; + let mut first_story_truth = 0; + let mut continuation_truth = 0; + for (&truth, &predicted) in truth_is_first.iter().zip(predicted_is_first) { + if truth { + first_story_truth += 1; + if predicted { + hits += 1; + } else { + misses += 1; + } + } else { + continuation_truth += 1; + if predicted { + false_alarms += 1; + } + } + } + Ok(FirstStoryRates { + hits, + misses, + false_alarms, + first_story_truth, + continuation_truth, + }) +} + +#[cfg(test)] +mod tests { + use super::{ + EventEvidenceLayer, FirstStoryRates, TdtStoryDecision, classify_tdt_story, + first_story_detection_rates, + }; + + #[test] + fn zero_denominator_rates_are_zero_and_track_is_not_first() { + assert_eq!(classify_tdt_story(&[7], 7), TdtStoryDecision::Track); + let empty_classes = FirstStoryRates { + hits: 0, + misses: 0, + false_alarms: 0, + first_story_truth: 0, + continuation_truth: 0, + }; + assert!(empty_classes.miss_rate() < 1e-15); + assert!(empty_classes.false_alarm_rate() < 1e-15); + let all_first = first_story_detection_rates(&[true, true], &[true, false]).expect("all"); + assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); + assert!(all_first.false_alarm_rate() < 1e-15); + assert_eq!( + EventEvidenceLayer::TdtDetection.wire_name(), + "tdt_detection" + ); + } +} diff --git a/crates/event_core/src/lib.rs b/crates/event_core/src/lib.rs index 25fd1022..9a1715b1 100644 --- a/crates/event_core/src/lib.rs +++ b/crates/event_core/src/lib.rs @@ -4,12 +4,15 @@ //! //! TEPP separates **fallible event mentions** grounded in evidence from //! **versioned event instances** used for temporal state, multilevel membership, -//! and scientific estimation. Mentions never silently become instances. +//! and scientific estimation. Mentions never silently become instances. TDT +//! detections and CHRONOS predictions remain measurement or hypothesis +//! artifacts until independently promoted. mod confidence; mod error; mod identifier; mod instance; +mod intelligence; mod mention; mod registry; mod role; @@ -28,6 +31,18 @@ pub use instance::EVENT_INSTANCE_WIRE_SCHEMA_VERSION; pub use instance::EventInstance; /// Explicit refusal to cast a mention as an instance. pub use instance::refuse_mention_as_instance; +/// Epistemic layer of an event-intelligence output. +pub use intelligence::EventEvidenceLayer; +/// Known-truth first-story detection counts. +pub use intelligence::FirstStoryRates; +/// First-story versus subsequent-track decision. +pub use intelligence::TdtStoryDecision; +/// Admit only promoted transitions into the forward state graph. +pub use intelligence::admit_state_transition; +/// Classify a candidate story as first-story or track. +pub use intelligence::classify_tdt_story; +/// Score first-story detections against a known stream. +pub use intelligence::first_story_detection_rates; /// Fallible textual event mention. pub use mention::EventMention; /// In-memory registry separating mentions from instances. diff --git a/crates/event_core/tests/intelligence_status_contract.rs b/crates/event_core/tests/intelligence_status_contract.rs new file mode 100644 index 00000000..eaa4fe92 --- /dev/null +++ b/crates/event_core/tests/intelligence_status_contract.rs @@ -0,0 +1,81 @@ +//! TDT/CHRONOS outputs cannot become transitions or historical facts. + +use event_core::{ + EventError, EventEvidenceLayer, TdtStoryDecision, admit_state_transition, classify_tdt_story, + first_story_detection_rates, +}; + +#[test] +fn only_promoted_transitions_enter_the_state_graph() { + admit_state_transition(EventEvidenceLayer::PromotedTransition).expect("promoted"); + assert!(EventEvidenceLayer::PromotedTransition.may_admit_state_transition()); + + assert_eq!( + admit_state_transition(EventEvidenceLayer::TdtDetection), + Err(EventError::DetectionIsNotTransition) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::ObservedMention), + Err(EventError::DetectionIsNotTransition) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::ChronosPrediction), + Err(EventError::PredictionIsNotFact) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::TemporalConsistency), + Err(EventError::DetectionIsNotTransition) + ); + + for layer in [ + EventEvidenceLayer::ObservedMention, + EventEvidenceLayer::TdtDetection, + EventEvidenceLayer::ChronosPrediction, + EventEvidenceLayer::TemporalConsistency, + EventEvidenceLayer::PromotedTransition, + ] { + assert!(!layer.wire_name().is_empty()); + } +} + +#[test] +fn first_story_detector_recovers_known_stream_with_computed_rates() { + // Appearance order of news stories: first occurrence is a first story. + let stream = [10_u64, 20, 10, 30, 20]; + let mut seen = Vec::new(); + let mut predicted = Vec::new(); + let mut truth = Vec::new(); + for story in stream { + let decision = classify_tdt_story(&seen, story); + predicted.push(matches!(decision, TdtStoryDecision::FirstStory)); + truth.push(!seen.contains(&story)); + if !seen.contains(&story) { + seen.push(story); + } + } + let rates = first_story_detection_rates(&truth, &predicted).expect("rates"); + assert_eq!(rates.hits(), 3); + assert_eq!(rates.misses(), 0); + assert_eq!(rates.false_alarms(), 0); + assert!(rates.miss_rate() < 1e-15); + assert!(rates.false_alarm_rate() < 1e-15); + + let always_first = [true, true, true, true, true]; + let noisy = first_story_detection_rates(&truth, &always_first).expect("noisy"); + assert_eq!(noisy.false_alarms(), 2); + let expected_fa = 2.0 / 2.0; + assert!((noisy.false_alarm_rate() - expected_fa).abs() < 1e-15); + assert_eq!(noisy.misses(), 0); +} + +#[test] +fn first_story_rates_fail_closed_on_empty_or_mismatched_streams() { + assert_eq!( + first_story_detection_rates(&[], &[]), + Err(EventError::InvalidWirePayload) + ); + assert_eq!( + first_story_detection_rates(&[true], &[true, false]), + Err(EventError::InvalidWirePayload) + ); +} diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 051062ea..4f2a49ad 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -13,7 +13,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | six distinct clocks and uncertain intervals | PRD; ADR 0002 | PR #8 `temporal_core` on protected main; PR #5 historical only | implemented-main | | 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 | implemented-main | -| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; full intelligence stack remaining | partial | +| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; ADR 0016 status gates on the active PR | 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 | | 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 | @@ -30,7 +30,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | compositional topic correlation / stable clustering | ADR 0005/0012; research | future `network_analysis` | accepted-target | | posterior ESEM / longitudinal invariance / DSEM | ADR 0005 | future `psychometric_core` | accepted-target | | CPU bounded multithreading + GPU/VRAM streaming/parity | ADR 0001/0006 | future `compute_backend` | accepted-target | -| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | +| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` evidence-layer admission + first-story rates on the active PR; full TDT/CHRONOS stack remaining | partial | | evidence-bounded LLM interpretation | ADR 0010/0012; PRD | future `interpretation_gateway` | accepted-target | | adaptive direct/verify/committee/conductor test-time compute | ADR 0010; `docs/LLM_ORCHESTRATION.md` | future contextual-orchestrator integration + ablation evidence | accepted-target | | purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | future authorization/persistence/export/provider adapters | accepted-target | diff --git a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md index b85ee0b4..32915ced 100644 --- a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md +++ b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md @@ -1,7 +1,7 @@ # ADR 0016 — TDT, CHRONOS, and Event Ontology intelligence boundary **Decision status:** Accepted -**Implementation maturity:** accepted-target +**Implementation maturity:** partial — evidence-layer admission gates and first-story miss/false-alarm scoring are implemented on the active PR and are not implemented-main until exact-head checks, review, and protected-main integration complete; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target **Date:** 2026-08-12 **Supersedes:** None; complements ADR 0002 temporal semantics and ADR 0003 event ontology/membership. diff --git a/docs/adr/README.md b/docs/adr/README.md index 1a9a7b31..ba29bc0e 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -21,7 +21,7 @@ Read [`ADR_POLICY.md`](ADR_POLICY.md) first. **Decision status and implementatio | [0013](0013-bitemporal-persistence-reproducibility-and-split-authority.md) | Bitemporal persistence, reproducibility manifests, and relation-aware split authority | Accepted | partial | Owns PostgreSQL adapter semantics, immutable run/split manifests, leakage-safe partitions, and recovery identity; optional `live-sqlx` `PgPool`, live PG CI, and tenant RLS implemented; full physical ERD remaining. | | [0014](0014-scientific-claim-promotion-and-release-evidence.md) | Scientific claim promotion and release evidence authority | Accepted | partial | Separates design, implementation, scientific/product claim, and release authority; repository SBOM/provenance generator implemented, full release bundle remaining. | | [0015](0015-autonomous-development-review-and-merge-authority.md) | Autonomous development, review, and merge authority separation | Accepted | active-PR | Separates model proposal, deterministic verification, publication, independent review, and merge/release authority. | -| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | accepted-target | Separates observed evidence, detection/tracking, prediction/schema inference, temporal consistency, and promoted transition authority. | +| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | partial | Evidence-layer admission and first-story rates are on the active PR; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. | ## Decision ownership summary diff --git a/docs/research/event-intelligence-status-gates.md b/docs/research/event-intelligence-status-gates.md new file mode 100644 index 00000000..207af365 --- /dev/null +++ b/docs/research/event-intelligence-status-gates.md @@ -0,0 +1,30 @@ +# Event-intelligence status gates + +## Scope + +This note doctors the first ADR 0016 production slice in `event_core`: + +1. every event-intelligence output carries an epistemic layer (`observed_mention`, `tdt_detection`, `chronos_prediction`, `temporal_consistency`, `promoted_transition`); +2. only an independently promoted transition may enter the forward state graph; +3. CHRONOS predictions are never treated as observed fact; +4. a first-story detector is scored with miss and false-alarm rates against a known story stream. + +Full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. No database migration is allocated. + +## Authoritative sources + +Allan, J. (Ed.). (2002). *Topic detection and tracking: Event-based information organization*. Kluwer Academic Publishers. + +Anagnostopoulos, E., Batsakis, S., & Petrakis, E. G. M. (2013). CHRONOS: A reasoning engine for qualitative temporal information in OWL. *Procedia Computer Science, 22*, 70–77. https://doi.org/10.1016/j.procs.2013.09.082 + +## Application + +Allan (2002) defines first-story detection as a scored measurement task with miss and false-alarm rates, not as automatic promotion into a chronology. Anagnostopoulos et al. (2013) keep qualitative temporal reasoning distinct from asserted event identity. TEPP therefore refuses to admit TDT detections or CHRONOS predictions as state transitions and reports computed first-story rates on a known stream (Allan, 2002; Anagnostopoulos et al., 2013). + +## Verification + +- `admit_state_transition(PromotedTransition)` succeeds; +- TDT/mention/consistency layers return `DetectionIsNotTransition`; +- CHRONOS predictions return `PredictionIsNotFact`; +- stream `[10,20,10,30,20]` recovers three first stories with miss rate 0 and false-alarm rate 0; +- an always-first detector yields a computed false-alarm rate of 1.0 on the two continuations. diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index 984d329c..bab4cc04 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -23,6 +23,7 @@ This report tracks exact-head scientific and engineering evidence required befor | Truth corpora / manifests | `tepp_simulation` | implemented-main | — | deterministic generator tests | Task 10 / PR #18 | | Recovery metrics | `validation_core` | implemented-main | — | RMSE/bias/coverage/MC gates | Task 11 / PR #19 | | Versioned API/export contracts | `tepp_api` | implemented-main | — | unknown-field/version/limit tests | Task 12 / PR #21; HTTP service remaining | +| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + first-story rates | known-stream miss/FA | ADR 0016; `docs/research/event-intelligence-status-gates.md` | | CWL modular connectors | `docs/connectors/*` | implemented-main | — | contract docs + examples | PR #22; live HTTP ports remaining | | Release SBOM/provenance generator | `scripts/release_evidence.py` | partial | — | generate+validate in CI | Task 13 partial / PR #28 | From 31ca1870992a5ede520d9664a6c450a204bdf2a8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:11:10 +0900 Subject: [PATCH 02/19] test(event): add independent known-identity baseline contract --- .../repair_pr50_add_known_identity_tests.py | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 scripts/repair_pr50_add_known_identity_tests.py diff --git a/scripts/repair_pr50_add_known_identity_tests.py b/scripts/repair_pr50_add_known_identity_tests.py new file mode 100644 index 00000000..9581ee08 --- /dev/null +++ b/scripts/repair_pr50_add_known_identity_tests.py @@ -0,0 +1,114 @@ +"""Add the PR 50 known-identity baseline regression before implementation.""" + +from pathlib import Path + + +CONTRACT = r'''//! Event-intelligence status gates and an explicitly oracle-assisted baseline. + +use event_core::{ + EventError, EventEvidenceLayer, FirstStoryRates, KnownIdentityStoryDecision, + admit_state_transition, classify_known_identity_baseline, first_story_detection_rates, +}; + +#[test] +fn only_promoted_transitions_enter_the_state_graph() { + admit_state_transition(EventEvidenceLayer::PromotedTransition).expect("promoted"); + assert!(EventEvidenceLayer::PromotedTransition.may_admit_state_transition()); + + assert_eq!( + admit_state_transition(EventEvidenceLayer::TdtDetection), + Err(EventError::DetectionIsNotTransition) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::ObservedMention), + Err(EventError::DetectionIsNotTransition) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::ChronosPrediction), + Err(EventError::PredictionIsNotFact) + ); + assert_eq!( + admit_state_transition(EventEvidenceLayer::TemporalConsistency), + Err(EventError::DetectionIsNotTransition) + ); + + for layer in [ + EventEvidenceLayer::ObservedMention, + EventEvidenceLayer::TdtDetection, + EventEvidenceLayer::ChronosPrediction, + EventEvidenceLayer::TemporalConsistency, + EventEvidenceLayer::PromotedTransition, + ] { + assert!(!layer.wire_name().is_empty()); + } +} + +#[test] +fn known_identity_baseline_is_scored_against_an_independent_truth_fixture() { + // This is an oracle-assisted identity baseline, not a raw-text first-story detector. + let stream = [10_u64, 20, 10, 30, 20]; + let truth_is_first = [true, true, false, true, false]; + let mut seen = Vec::new(); + let mut predicted_is_first = Vec::new(); + + for story_identity in stream { + let decision = classify_known_identity_baseline(&seen, story_identity); + predicted_is_first.push(matches!( + decision, + KnownIdentityStoryDecision::FirstOccurrence + )); + if matches!(decision, KnownIdentityStoryDecision::FirstOccurrence) { + seen.push(story_identity); + } + } + + let rates = first_story_detection_rates(&truth_is_first, &predicted_is_first) + .expect("independent truth fixture"); + assert_eq!(rates.hits(), 3); + assert_eq!(rates.misses(), 0); + assert_eq!(rates.false_alarms(), 0); + assert!(rates.miss_rate() < 1e-15); + assert!(rates.false_alarm_rate() < 1e-15); + + let always_first = [true, true, true, true, true]; + let false_alarm_rates = first_story_detection_rates(&truth_is_first, &always_first) + .expect("false-alarm fixture"); + assert_eq!(false_alarm_rates.false_alarms(), 2); + assert!((false_alarm_rates.false_alarm_rate() - 1.0).abs() < 1e-15); + + let always_continuation = [false, false, false, false, false]; + let miss_rates = first_story_detection_rates(&truth_is_first, &always_continuation) + .expect("miss fixture"); + assert_eq!(miss_rates.misses(), 3); + assert!((miss_rates.miss_rate() - 1.0).abs() < 1e-15); + assert!(miss_rates.false_alarm_rate() < 1e-15); +} + +#[test] +fn baseline_and_rate_contracts_fail_closed_at_their_boundaries() { + assert_eq!( + classify_known_identity_baseline(&[7], 7), + KnownIdentityStoryDecision::RepeatedIdentity + ); + assert_eq!( + classify_known_identity_baseline(&[7], 8), + KnownIdentityStoryDecision::FirstOccurrence + ); + assert_eq!( + first_story_detection_rates(&[], &[]), + Err(EventError::InvalidWirePayload) + ); + assert_eq!( + first_story_detection_rates(&[true], &[true, false]), + Err(EventError::InvalidWirePayload) + ); + + let empty_classes = FirstStoryRates::empty_for_test(); + assert!(empty_classes.miss_rate() < 1e-15); + assert!(empty_classes.false_alarm_rate() < 1e-15); +} +''' + +path = Path("crates/event_core/tests/intelligence_status_contract.rs") +path.parent.mkdir(parents=True, exist_ok=True) +path.write_text(CONTRACT, encoding="utf-8") From 5e0c6e6d9469c67825812c7f222cf016f58e590d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:11:58 +0900 Subject: [PATCH 03/19] test(event): avoid test-only public constructor --- scripts/repair_pr50_add_known_identity_tests.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/scripts/repair_pr50_add_known_identity_tests.py b/scripts/repair_pr50_add_known_identity_tests.py index 9581ee08..ada2651b 100644 --- a/scripts/repair_pr50_add_known_identity_tests.py +++ b/scripts/repair_pr50_add_known_identity_tests.py @@ -6,8 +6,8 @@ CONTRACT = r'''//! Event-intelligence status gates and an explicitly oracle-assisted baseline. use event_core::{ - EventError, EventEvidenceLayer, FirstStoryRates, KnownIdentityStoryDecision, - admit_state_transition, classify_known_identity_baseline, first_story_detection_rates, + EventError, EventEvidenceLayer, KnownIdentityStoryDecision, admit_state_transition, + classify_known_identity_baseline, first_story_detection_rates, }; #[test] @@ -103,9 +103,13 @@ Err(EventError::InvalidWirePayload) ); - let empty_classes = FirstStoryRates::empty_for_test(); - assert!(empty_classes.miss_rate() < 1e-15); - assert!(empty_classes.false_alarm_rate() < 1e-15); + let all_continuations = first_story_detection_rates(&[false, false], &[false, false]) + .expect("zero first-story denominator"); + assert!(all_continuations.miss_rate() < 1e-15); + let all_first = first_story_detection_rates(&[true, true], &[true, false]) + .expect("zero continuation denominator"); + assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); + assert!(all_first.false_alarm_rate() < 1e-15); } ''' From 786436a4822b772a7f8ad1da5ddda2979393acf0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:13:30 +0900 Subject: [PATCH 04/19] fix(event): narrow first-story logic to a known-identity baseline --- scripts/repair_pr50_apply_known_identity.py | 398 ++++++++++++++++++++ 1 file changed, 398 insertions(+) create mode 100644 scripts/repair_pr50_apply_known_identity.py diff --git a/scripts/repair_pr50_apply_known_identity.py b/scripts/repair_pr50_apply_known_identity.py new file mode 100644 index 00000000..6342c9c6 --- /dev/null +++ b/scripts/repair_pr50_apply_known_identity.py @@ -0,0 +1,398 @@ +"""Apply the PR 50 event-status and known-identity baseline repair.""" + +from pathlib import Path + + +def replace_once(path: str, old: str, new: str) -> None: + """Replace one exact fragment or fail closed.""" + file_path = Path(path) + text = file_path.read_text(encoding="utf-8") + count = text.count(old) + if count != 1: + raise SystemExit(f"{path}: expected one replacement target, found {count}") + file_path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +INTELLIGENCE = r'''//! Evidence-status gates and oracle-assisted story-identity baselines. + +use crate::EventError; + +/// Epistemic layer of an event-intelligence output. +/// +/// Only [`EventEvidenceLayer::PromotedTransition`] may enter the forward +/// state/input-process-outcome graph. TDT detections and CHRONOS predictions +/// remain measurement or hypothesis artifacts. +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum EventEvidenceLayer { + /// Fallible textual mention grounded in evidence. + ObservedMention, + /// TDT-style detection, link, or track output. + TdtDetection, + /// CHRONOS-style schema completion or predicted event. + ChronosPrediction, + /// Symbolic temporal-consistency judgment. + TemporalConsistency, + /// Independently promoted forward state transition. + PromotedTransition, +} + +impl EventEvidenceLayer { + /// Stable wire name for this layer. + #[must_use] + pub const fn wire_name(self) -> &'static str { + match self { + Self::ObservedMention => "observed_mention", + Self::TdtDetection => "tdt_detection", + Self::ChronosPrediction => "chronos_prediction", + Self::TemporalConsistency => "temporal_consistency", + Self::PromotedTransition => "promoted_transition", + } + } + + /// Whether this layer may admit a forward state-transition edge. + #[must_use] + pub const fn may_admit_state_transition(self) -> bool { + matches!(self, Self::PromotedTransition) + } +} + +/// Admit a layer into the forward state graph or fail closed. +/// +/// # Errors +/// +/// Returns [`EventError::PredictionIsNotFact`] for CHRONOS predictions and +/// [`EventError::DetectionIsNotTransition`] for every other non-promoted layer. +pub fn admit_state_transition(layer: EventEvidenceLayer) -> Result<(), EventError> { + if layer.may_admit_state_transition() { + Ok(()) + } else if matches!(layer, EventEvidenceLayer::ChronosPrediction) { + Err(EventError::PredictionIsNotFact) + } else { + Err(EventError::DetectionIsNotTransition) + } +} + +/// Oracle-assisted identity-baseline decision for one candidate story. +/// +/// This baseline assumes a stable gold or externally adjudicated story identity. +/// It is not a detector over raw text, embeddings, or document metadata. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum KnownIdentityStoryDecision { + /// The externally supplied story identity has not appeared before. + FirstOccurrence, + /// The externally supplied story identity already appeared in the stream. + RepeatedIdentity, +} + +/// Classify one externally identified story against identities already observed. +/// +/// This function is an oracle-assisted baseline for scoring and regression +/// tests. Product code must not present it as first-story detection from raw +/// documents. +#[must_use] +pub fn classify_known_identity_baseline( + seen_story_ids: &[u64], + candidate_story_id: u64, +) -> KnownIdentityStoryDecision { + if seen_story_ids.contains(&candidate_story_id) { + KnownIdentityStoryDecision::RepeatedIdentity + } else { + KnownIdentityStoryDecision::FirstOccurrence + } +} + +/// Known-truth first-story detection counts. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct FirstStoryRates { + hits: usize, + misses: usize, + false_alarms: usize, + first_story_truth: usize, + continuation_truth: usize, +} + +impl FirstStoryRates { + /// Correct first-story detections. + #[must_use] + pub const fn hits(self) -> usize { + self.hits + } + + /// Missed first stories. + #[must_use] + pub const fn misses(self) -> usize { + self.misses + } + + /// Continuations labeled as first stories. + #[must_use] + pub const fn false_alarms(self) -> usize { + self.false_alarms + } + + /// Miss rate among true first stories. + #[must_use] + pub fn miss_rate(self) -> f64 { + if self.first_story_truth == 0 { + 0.0 + } else { + #[allow(clippy::cast_precision_loss)] + { + self.misses as f64 / self.first_story_truth as f64 + } + } + } + + /// False-alarm rate among true continuations. + #[must_use] + pub fn false_alarm_rate(self) -> f64 { + if self.continuation_truth == 0 { + 0.0 + } else { + #[allow(clippy::cast_precision_loss)] + { + self.false_alarms as f64 / self.continuation_truth as f64 + } + } + } +} + +/// Score predicted first-story labels against an independently supplied truth. +/// +/// # Errors +/// +/// Returns [`EventError::InvalidWirePayload`] when the streams are empty or +/// have unequal length. +pub fn first_story_detection_rates( + truth_is_first: &[bool], + predicted_is_first: &[bool], +) -> Result { + if truth_is_first.is_empty() || truth_is_first.len() != predicted_is_first.len() { + return Err(EventError::InvalidWirePayload); + } + let mut hits = 0; + let mut misses = 0; + let mut false_alarms = 0; + let mut first_story_truth = 0; + let mut continuation_truth = 0; + for (&truth, &predicted) in truth_is_first.iter().zip(predicted_is_first) { + if truth { + first_story_truth += 1; + if predicted { + hits += 1; + } else { + misses += 1; + } + } else { + continuation_truth += 1; + if predicted { + false_alarms += 1; + } + } + } + Ok(FirstStoryRates { + hits, + misses, + false_alarms, + first_story_truth, + continuation_truth, + }) +} + +#[cfg(test)] +mod tests { + use super::{ + EventEvidenceLayer, FirstStoryRates, KnownIdentityStoryDecision, + classify_known_identity_baseline, first_story_detection_rates, + }; + + #[test] + fn zero_denominator_rates_are_zero_and_identity_baseline_is_explicit() { + assert_eq!( + classify_known_identity_baseline(&[7], 7), + KnownIdentityStoryDecision::RepeatedIdentity + ); + assert_eq!( + classify_known_identity_baseline(&[7], 8), + KnownIdentityStoryDecision::FirstOccurrence + ); + let empty_classes = FirstStoryRates { + hits: 0, + misses: 0, + false_alarms: 0, + first_story_truth: 0, + continuation_truth: 0, + }; + assert!(empty_classes.miss_rate() < 1e-15); + assert!(empty_classes.false_alarm_rate() < 1e-15); + let all_first = first_story_detection_rates(&[true, true], &[true, false]).expect("all"); + assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); + assert!(all_first.false_alarm_rate() < 1e-15); + assert_eq!( + EventEvidenceLayer::TdtDetection.wire_name(), + "tdt_detection" + ); + } +} +''' + +Path("crates/event_core/src/intelligence.rs").write_text(INTELLIGENCE, encoding="utf-8") + +replace_once( + "crates/event_core/src/error.rs", + """ /// An unknown event-role name was supplied. + UnknownEventRole, +""", + """ /// An unknown event-role name was supplied. + UnknownEventRole, + /// A TDT detection or mention was treated as a state transition. + DetectionIsNotTransition, + /// A CHRONOS prediction was treated as an observed or promoted fact. + PredictionIsNotFact, +""", +) +replace_once( + "crates/event_core/src/error.rs", + """ Self::UnknownEventRole => \"unknown event role\", +""", + """ Self::UnknownEventRole => \"unknown event role\", + Self::DetectionIsNotTransition => \"detection is not a state transition\", + Self::PredictionIsNotFact => \"prediction is not an observed fact\", +""", +) +replace_once( + "crates/event_core/src/error.rs", + """ (EventError::UnknownEventRole, \"unknown event role\"), +""", + """ (EventError::UnknownEventRole, \"unknown event role\"), + ( + EventError::DetectionIsNotTransition, + \"detection is not a state transition\", + ), + ( + EventError::PredictionIsNotFact, + \"prediction is not an observed fact\", + ), +""", +) + +replace_once( + "crates/event_core/src/lib.rs", + """mod identifier; +mod instance; +""", + """mod identifier; +mod intelligence; +mod instance; +""", +) +replace_once( + "crates/event_core/src/lib.rs", + """/// Opaque event-instance identifier. +pub use identifier::EventInstanceId; +""", + """/// Opaque event-instance identifier. +pub use identifier::EventInstanceId; +/// Admit only independently promoted state transitions. +pub use intelligence::admit_state_transition; +/// Oracle-assisted classification using externally supplied story identities. +pub use intelligence::classify_known_identity_baseline; +/// Score first-story predictions against independent known truth. +pub use intelligence::first_story_detection_rates; +/// Epistemic layer for event-intelligence output. +pub use intelligence::EventEvidenceLayer; +/// First-story miss and false-alarm summary. +pub use intelligence::FirstStoryRates; +/// Decision from the oracle-assisted known-identity baseline. +pub use intelligence::KnownIdentityStoryDecision; +""", +) + +RESEARCH = r'''# Event-intelligence status gates and known-identity baseline + +## Scope + +This note doctors the first ADR 0016 production slice in `event_core`: + +1. every event-intelligence output carries an epistemic layer (`observed_mention`, `tdt_detection`, `chronos_prediction`, `temporal_consistency`, `promoted_transition`); +2. only an independently promoted transition may enter the forward state graph; +3. CHRONOS predictions are never treated as observed fact; +4. generic first-story miss and false-alarm rates are scored against an independently supplied truth vector; +5. the committed story-identity classifier is explicitly an oracle-assisted known-identity baseline, not a detector over raw documents. + +Full raw-text TDT detection, linking, tracking, calibration, and CHRONOS schema extraction remain accepted-target. No database migration is allocated. + +## Authoritative sources + +Allan, J. (Ed.). (2002). *Topic detection and tracking: Event-based information organization*. Kluwer Academic Publishers. + +Anagnostopoulos, E., Batsakis, S., & Petrakis, E. G. M. (2013). CHRONOS: A reasoning engine for qualitative temporal information in OWL. *Procedia Computer Science, 22*, 70–77. https://doi.org/10.1016/j.procs.2013.09.082 + +## Application + +Allan (2002) defines first-story detection as a scored measurement task with miss and false-alarm rates, not as automatic promotion into a chronology. A repeated externally supplied story identifier can provide a deterministic oracle baseline for regression testing, but it cannot establish detection performance from text because the identity already contains the answer. Anagnostopoulos et al. (2013) keep qualitative temporal reasoning distinct from asserted event identity. TEPP therefore refuses to admit TDT detections or CHRONOS predictions as state transitions, exposes generic rate scoring against independent truth, and labels identity-membership logic as a baseline rather than a detector. + +## Verification + +- `admit_state_transition(PromotedTransition)` succeeds; +- TDT/mention/consistency layers return `DetectionIsNotTransition`; +- CHRONOS predictions return `PredictionIsNotFact`; +- the identity stream `[10,20,10,30,20]` is scored against the independently fixed truth vector `[true,true,false,true,false]`; +- always-first and always-continuation predictions exercise false-alarm and miss paths; +- empty and unequal truth/prediction vectors fail closed; +- no product or scientific claim treats the known-identity baseline as raw-text first-story detection. +''' +Path("docs/research/event-intelligence-status-gates.md").write_text(RESEARCH, encoding="utf-8") + +changelog_path = Path("CHANGELOG.md") +changelog = changelog_path.read_text(encoding="utf-8") +bullet = "- `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; generic first-story miss/false-alarm scoring is paired with an explicitly oracle-assisted known-identity baseline.\n" +if bullet not in changelog: + marker = "### Added\n\n" + if changelog.count(marker) != 1: + raise SystemExit("CHANGELOG Added marker mismatch") + changelog = changelog.replace(marker, marker + bullet, 1) +changelog_path.write_text(changelog, encoding="utf-8") + +replace_once( + "DOCUMENTATION.md", + """| Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | +""", + """| Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | +| Event-intelligence status-gate doctoring | [`docs/research/event-intelligence-status-gates.md`](docs/research/event-intelligence-status-gates.md) | +""", +) +replace_once( + "docs/TRACEABILITY.md", + """| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; full intelligence stack remaining | partial | +""", + """| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; ADR 0016 evidence-status gates on the active PR | partial | +""", +) +replace_once( + "docs/TRACEABILITY.md", + """| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | +""", + """| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` admission gates, generic rates, and a known-identity baseline on the active PR; raw-text TDT/CHRONOS stack remaining | partial | +""", +) +replace_once( + "docs/adr/0016-tdt-chronos-event-intelligence-boundary.md", + "**Implementation maturity:** accepted-target \n", + "**Implementation maturity:** partial — evidence-layer admission gates, generic first-story rate scoring, and an oracle-assisted known-identity baseline are implemented on the active PR; raw-text TDT tracking/calibration and CHRONOS schema extraction remain accepted-target\n", +) +replace_once( + "docs/adr/README.md", + """| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | accepted-target | Separates observed evidence, detection/tracking, prediction/schema inference, temporal consistency, and promoted transition authority. | +""", + """| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | partial | Admission gates, generic first-story rates, and a known-identity baseline are on the active PR; raw-text TDT/CHRONOS work remains accepted-target. | +""", +) +replace_once( + "docs/validation/temporal-event-foundation.md", + """| Versioned API/export contracts | `tepp_api` | implemented-main | — | unknown-field/version/limit tests | Task 12 / PR #21; HTTP service remaining | +""", + """| Versioned API/export contracts | `tepp_api` | implemented-main | — | unknown-field/version/limit tests | Task 12 / PR #21; HTTP service remaining | +| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + generic first-story rates | independent truth + known-identity baseline | ADR 0016; `docs/research/event-intelligence-status-gates.md` | +""", +) From 90b1af1503b41f09106dbdf5a892de8fb9e8f5ba Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:14:08 +0900 Subject: [PATCH 05/19] ci(event): verify PR 50 known-identity baseline repair --- .../repair-pr50-known-identity-baseline.yml | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .github/workflows/repair-pr50-known-identity-baseline.yml diff --git a/.github/workflows/repair-pr50-known-identity-baseline.yml b/.github/workflows/repair-pr50-known-identity-baseline.yml new file mode 100644 index 00000000..3e36301b --- /dev/null +++ b/.github/workflows/repair-pr50-known-identity-baseline.yml @@ -0,0 +1,84 @@ +name: Repair PR 50 known-identity baseline + +on: + pull_request: + types: + - synchronize + - reopened + - ready_for_review + +permissions: + contents: read + +concurrency: + group: repair-tepp-pr-50-known-identity-baseline + cancel-in-progress: true + +jobs: + repair: + if: >- + github.event.pull_request.number == 50 && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'agent/event-intelligence-status-gates' + runs-on: ubuntu-latest + timeout-minutes: 40 + permissions: + contents: write + steps: + - name: Checkout exact PR branch + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: agent/event-intelligence-status-gates + fetch-depth: 0 + persist-credentials: true + + - name: Merge current protected main + run: | + git fetch origin main + git merge --no-edit -X theirs origin/main + + - name: Install pinned Rust toolchain + run: rustup toolchain install 1.97.1 --profile minimal --component clippy --component rustfmt + + - name: Add independent-truth regression contract + run: python3 scripts/repair_pr50_add_known_identity_tests.py + + - name: Prove the old detector claim is RED + run: | + set +e + output=$(cargo +1.97.1 test -p event_core --test intelligence_status_contract 2>&1) + status=$? + set -e + printf '%s\n' "$output" + if [ "$status" -eq 0 ]; then + echo "Expected the old API to lack the known-identity baseline boundary" >&2 + exit 1 + fi + grep -E "KnownIdentityStoryDecision|classify_known_identity_baseline" <<<"$output" + + - name: Apply status-gate and baseline implementation + run: | + python3 scripts/repair_pr50_apply_known_identity.py + cargo +1.97.1 fmt --all + + - name: Verify focused and workspace contracts + run: | + cargo +1.97.1 fmt --all --check + cargo +1.97.1 test -p event_core --all-features + cargo +1.97.1 clippy -p event_core --all-targets --all-features -- -D warnings + cargo +1.97.1 test --workspace --all-features + python3 scripts/check_workspace_contract.py + python3 scripts/check_docstrings.py + python3 scripts/validate_documentation.py + + - name: Commit verified repair and remove one-shot files + run: | + rm -f .github/workflows/repair-pr50-known-identity-baseline.yml + rm -f scripts/repair_pr50_add_known_identity_tests.py + rm -f scripts/repair_pr50_apply_known_identity.py + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix(event): label known-identity logic as an oracle baseline" + git push origin HEAD:agent/event-intelligence-status-gates From b5ae04fd4697aca8711cccd248777072596bd24b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:36:01 +0900 Subject: [PATCH 06/19] ci(event): activate PR 50 repair through registered workflow --- .github/workflows/docs-quality.yml | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml index eae33b97..a4757ca9 100644 --- a/.github/workflows/docs-quality.yml +++ b/.github/workflows/docs-quality.yml @@ -7,6 +7,8 @@ on: - "**/*.json" - ".github/workflows/**" - "scripts/validate_documentation.py" + - "scripts/repair_pr50_*.py" + - "crates/event_core/**" push: branches: - main @@ -38,3 +40,67 @@ jobs: run: python3 scripts/validate_documentation.py - name: Reject whitespace errors run: git diff --check HEAD^ HEAD || git diff --check + + repair-pr50: + name: Repair known-identity baseline claim + if: >- + github.event_name == 'pull_request' && + github.event.pull_request.number == 50 && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'agent/event-intelligence-status-gates' + runs-on: ubuntu-latest + timeout-minutes: 40 + permissions: + contents: write + steps: + - name: Checkout exact PR branch + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: agent/event-intelligence-status-gates + fetch-depth: 0 + persist-credentials: true + - name: Merge current protected main + run: | + git fetch origin main + git merge --no-edit origin/main + - name: Install pinned Rust toolchain + run: rustup toolchain install 1.97.1 --profile minimal --component clippy --component rustfmt + - name: Add independent-truth regression contract + run: python3 scripts/repair_pr50_add_known_identity_tests.py + - name: Prove old detector claim is RED + run: | + set +e + output=$(cargo +1.97.1 test -p event_core --test intelligence_status_contract 2>&1) + status=$? + set -e + printf '%s\n' "$output" + if [ "$status" -eq 0 ]; then + echo "Expected the old API to lack the known-identity baseline boundary" >&2 + exit 1 + fi + grep -E "KnownIdentityStoryDecision|classify_known_identity_baseline" <<<"$output" + - name: Apply status-gate and baseline implementation + run: | + python3 scripts/repair_pr50_apply_known_identity.py + cargo +1.97.1 fmt --all + - name: Verify focused and workspace contracts + run: | + cargo +1.97.1 fmt --all --check + cargo +1.97.1 test -p event_core --all-features + cargo +1.97.1 clippy -p event_core --all-targets --all-features -- -D warnings + cargo +1.97.1 test --workspace --all-features + python3 scripts/check_workspace_contract.py + python3 scripts/check_docstrings.py + python3 scripts/validate_documentation.py + - name: Commit verified repair and remove one-shot files + run: | + git checkout origin/main -- .github/workflows/docs-quality.yml + rm -f .github/workflows/repair-pr50-known-identity-baseline.yml + rm -f scripts/repair_pr50_add_known_identity_tests.py + rm -f scripts/repair_pr50_apply_known_identity.py + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git diff --cached --check + git commit -m "fix(event): label known-identity logic as an oracle baseline" + git push origin HEAD:agent/event-intelligence-status-gates From a5e7609073bbeb004af0eb25dcbf7691a8cf8ea4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:38:36 +0900 Subject: [PATCH 07/19] ci(event): allow ready-for-review repair trigger --- .github/workflows/docs-quality.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml index a4757ca9..851e65b7 100644 --- a/.github/workflows/docs-quality.yml +++ b/.github/workflows/docs-quality.yml @@ -2,6 +2,10 @@ name: Documentation Quality on: pull_request: + types: + - synchronize + - reopened + - ready_for_review paths: - "**/*.md" - "**/*.json" From 955e5dab733983f2d9a529e0527755c742c7f34b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 22:48:09 +0900 Subject: [PATCH 08/19] ci(event): preserve protected-main traceability during repair --- .github/workflows/docs-quality.yml | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml index 851e65b7..73eba813 100644 --- a/.github/workflows/docs-quality.yml +++ b/.github/workflows/docs-quality.yml @@ -87,6 +87,43 @@ jobs: run: | python3 scripts/repair_pr50_apply_known_identity.py cargo +1.97.1 fmt --all + - name: Rebase shared ledgers on protected main + run: | + git show origin/main:CHANGELOG.md > /tmp/main_changelog.md + git show origin/main:docs/TRACEABILITY.md > /tmp/main_traceability.md + git show origin/main:docs/validation/temporal-event-foundation.md > /tmp/main_validation.md + python3 - <<'PY' + from pathlib import Path + + changelog = Path('/tmp/main_changelog.md').read_text(encoding='utf-8') + bullet = '- `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; generic first-story miss/false-alarm scoring is paired with an explicitly oracle-assisted known-identity baseline.\n' + if bullet not in changelog: + marker = '### Added\n\n' + if changelog.count(marker) != 1: + raise SystemExit('protected-main CHANGELOG Added marker mismatch') + changelog = changelog.replace(marker, marker + bullet, 1) + Path('CHANGELOG.md').write_text(changelog, encoding='utf-8') + + traceability = Path('/tmp/main_traceability.md').read_text(encoding='utf-8') + event_old = '| 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 |' + event_new = '| event ontology/evidence mentions | PRD; ADR 0003/0016 | `event_core` mention/instance separation on protected main; ADR 0016 admission gates and independent-truth rate scoring on the active PR; `persistence_postgres` mention SQL implemented-main refuses mention-as-instance; event-instance SQL (#39 implemented-main) refuses inverted windows; raw-text detection/tracking remains | partial |' + tdt_old = '| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target |' + tdt_new = '| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` admission gates, generic rates, and an explicitly oracle-assisted known-identity baseline on the active PR; raw-text TDT/CHRONOS stack remaining | partial |' + for old, new in ((event_old, event_new), (tdt_old, tdt_new)): + if traceability.count(old) != 1: + raise SystemExit(f'protected-main traceability target mismatch: {old}') + traceability = traceability.replace(old, new, 1) + Path('docs/TRACEABILITY.md').write_text(traceability, encoding='utf-8') + + validation = Path('/tmp/main_validation.md').read_text(encoding='utf-8') + row = '| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + generic first-story rates | independent truth + known-identity baseline | ADR 0016; `docs/research/event-intelligence-status-gates.md` |\n' + if row not in validation: + marker = '| 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 |\n' + if validation.count(marker) != 1: + raise SystemExit('protected-main validation marker mismatch') + validation = validation.replace(marker, marker + row, 1) + Path('docs/validation/temporal-event-foundation.md').write_text(validation, encoding='utf-8') + PY - name: Verify focused and workspace contracts run: | cargo +1.97.1 fmt --all --check From b8b256795b6dcb68b7de8a4b3cb7f713370710a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 18:33:23 +0900 Subject: [PATCH 09/19] fix(event): align known-identity repair preconditions --- docs/TRACEABILITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 4f2a49ad..051062ea 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -13,7 +13,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | six distinct clocks and uncertain intervals | PRD; ADR 0002 | PR #8 `temporal_core` on protected main; PR #5 historical only | implemented-main | | 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 | implemented-main | -| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; ADR 0016 status gates on the active PR | partial | +| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; 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 | | 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 | @@ -30,7 +30,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | compositional topic correlation / stable clustering | ADR 0005/0012; research | future `network_analysis` | accepted-target | | posterior ESEM / longitudinal invariance / DSEM | ADR 0005 | future `psychometric_core` | accepted-target | | CPU bounded multithreading + GPU/VRAM streaming/parity | ADR 0001/0006 | future `compute_backend` | accepted-target | -| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` evidence-layer admission + first-story rates on the active PR; full TDT/CHRONOS stack remaining | partial | +| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | | evidence-bounded LLM interpretation | ADR 0010/0012; PRD | future `interpretation_gateway` | accepted-target | | adaptive direct/verify/committee/conductor test-time compute | ADR 0010; `docs/LLM_ORCHESTRATION.md` | future contextual-orchestrator integration + ablation evidence | accepted-target | | purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | future authorization/persistence/export/provider adapters | accepted-target | From dc78049f3b40b700ed3401ca8773ce01b18bad61 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 18:58:09 +0900 Subject: [PATCH 10/19] fix(event): restore ADR repair precondition --- docs/adr/0016-tdt-chronos-event-intelligence-boundary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md index 32915ced..b85ee0b4 100644 --- a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md +++ b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md @@ -1,7 +1,7 @@ # ADR 0016 — TDT, CHRONOS, and Event Ontology intelligence boundary **Decision status:** Accepted -**Implementation maturity:** partial — evidence-layer admission gates and first-story miss/false-alarm scoring are implemented on the active PR and are not implemented-main until exact-head checks, review, and protected-main integration complete; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target +**Implementation maturity:** accepted-target **Date:** 2026-08-12 **Supersedes:** None; complements ADR 0002 temporal semantics and ADR 0003 event ontology/membership. From b4e0646800e313aaccbaa6b18eaa22565f6a0801 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 16:20:40 +0900 Subject: [PATCH 11/19] chore(ci): remove completed event repair loop --- .github/workflows/docs-quality.yml | 107 ----- .../repair-pr50-known-identity-baseline.yml | 84 ---- .../repair_pr50_add_known_identity_tests.py | 118 ------ scripts/repair_pr50_apply_known_identity.py | 398 ------------------ 4 files changed, 707 deletions(-) delete mode 100644 .github/workflows/repair-pr50-known-identity-baseline.yml delete mode 100644 scripts/repair_pr50_add_known_identity_tests.py delete mode 100644 scripts/repair_pr50_apply_known_identity.py diff --git a/.github/workflows/docs-quality.yml b/.github/workflows/docs-quality.yml index 73eba813..eae33b97 100644 --- a/.github/workflows/docs-quality.yml +++ b/.github/workflows/docs-quality.yml @@ -2,17 +2,11 @@ name: Documentation Quality on: pull_request: - types: - - synchronize - - reopened - - ready_for_review paths: - "**/*.md" - "**/*.json" - ".github/workflows/**" - "scripts/validate_documentation.py" - - "scripts/repair_pr50_*.py" - - "crates/event_core/**" push: branches: - main @@ -44,104 +38,3 @@ jobs: run: python3 scripts/validate_documentation.py - name: Reject whitespace errors run: git diff --check HEAD^ HEAD || git diff --check - - repair-pr50: - name: Repair known-identity baseline claim - if: >- - github.event_name == 'pull_request' && - github.event.pull_request.number == 50 && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.head.ref == 'agent/event-intelligence-status-gates' - runs-on: ubuntu-latest - timeout-minutes: 40 - permissions: - contents: write - steps: - - name: Checkout exact PR branch - uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 - with: - ref: agent/event-intelligence-status-gates - fetch-depth: 0 - persist-credentials: true - - name: Merge current protected main - run: | - git fetch origin main - git merge --no-edit origin/main - - name: Install pinned Rust toolchain - run: rustup toolchain install 1.97.1 --profile minimal --component clippy --component rustfmt - - name: Add independent-truth regression contract - run: python3 scripts/repair_pr50_add_known_identity_tests.py - - name: Prove old detector claim is RED - run: | - set +e - output=$(cargo +1.97.1 test -p event_core --test intelligence_status_contract 2>&1) - status=$? - set -e - printf '%s\n' "$output" - if [ "$status" -eq 0 ]; then - echo "Expected the old API to lack the known-identity baseline boundary" >&2 - exit 1 - fi - grep -E "KnownIdentityStoryDecision|classify_known_identity_baseline" <<<"$output" - - name: Apply status-gate and baseline implementation - run: | - python3 scripts/repair_pr50_apply_known_identity.py - cargo +1.97.1 fmt --all - - name: Rebase shared ledgers on protected main - run: | - git show origin/main:CHANGELOG.md > /tmp/main_changelog.md - git show origin/main:docs/TRACEABILITY.md > /tmp/main_traceability.md - git show origin/main:docs/validation/temporal-event-foundation.md > /tmp/main_validation.md - python3 - <<'PY' - from pathlib import Path - - changelog = Path('/tmp/main_changelog.md').read_text(encoding='utf-8') - bullet = '- `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; generic first-story miss/false-alarm scoring is paired with an explicitly oracle-assisted known-identity baseline.\n' - if bullet not in changelog: - marker = '### Added\n\n' - if changelog.count(marker) != 1: - raise SystemExit('protected-main CHANGELOG Added marker mismatch') - changelog = changelog.replace(marker, marker + bullet, 1) - Path('CHANGELOG.md').write_text(changelog, encoding='utf-8') - - traceability = Path('/tmp/main_traceability.md').read_text(encoding='utf-8') - event_old = '| 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 |' - event_new = '| event ontology/evidence mentions | PRD; ADR 0003/0016 | `event_core` mention/instance separation on protected main; ADR 0016 admission gates and independent-truth rate scoring on the active PR; `persistence_postgres` mention SQL implemented-main refuses mention-as-instance; event-instance SQL (#39 implemented-main) refuses inverted windows; raw-text detection/tracking remains | partial |' - tdt_old = '| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target |' - tdt_new = '| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` admission gates, generic rates, and an explicitly oracle-assisted known-identity baseline on the active PR; raw-text TDT/CHRONOS stack remaining | partial |' - for old, new in ((event_old, event_new), (tdt_old, tdt_new)): - if traceability.count(old) != 1: - raise SystemExit(f'protected-main traceability target mismatch: {old}') - traceability = traceability.replace(old, new, 1) - Path('docs/TRACEABILITY.md').write_text(traceability, encoding='utf-8') - - validation = Path('/tmp/main_validation.md').read_text(encoding='utf-8') - row = '| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + generic first-story rates | independent truth + known-identity baseline | ADR 0016; `docs/research/event-intelligence-status-gates.md` |\n' - if row not in validation: - marker = '| 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 |\n' - if validation.count(marker) != 1: - raise SystemExit('protected-main validation marker mismatch') - validation = validation.replace(marker, marker + row, 1) - Path('docs/validation/temporal-event-foundation.md').write_text(validation, encoding='utf-8') - PY - - name: Verify focused and workspace contracts - run: | - cargo +1.97.1 fmt --all --check - cargo +1.97.1 test -p event_core --all-features - cargo +1.97.1 clippy -p event_core --all-targets --all-features -- -D warnings - cargo +1.97.1 test --workspace --all-features - python3 scripts/check_workspace_contract.py - python3 scripts/check_docstrings.py - python3 scripts/validate_documentation.py - - name: Commit verified repair and remove one-shot files - run: | - git checkout origin/main -- .github/workflows/docs-quality.yml - rm -f .github/workflows/repair-pr50-known-identity-baseline.yml - rm -f scripts/repair_pr50_add_known_identity_tests.py - rm -f scripts/repair_pr50_apply_known_identity.py - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix(event): label known-identity logic as an oracle baseline" - git push origin HEAD:agent/event-intelligence-status-gates diff --git a/.github/workflows/repair-pr50-known-identity-baseline.yml b/.github/workflows/repair-pr50-known-identity-baseline.yml deleted file mode 100644 index 3e36301b..00000000 --- a/.github/workflows/repair-pr50-known-identity-baseline.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Repair PR 50 known-identity baseline - -on: - pull_request: - types: - - synchronize - - reopened - - ready_for_review - -permissions: - contents: read - -concurrency: - group: repair-tepp-pr-50-known-identity-baseline - cancel-in-progress: true - -jobs: - repair: - if: >- - github.event.pull_request.number == 50 && - github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.head.ref == 'agent/event-intelligence-status-gates' - runs-on: ubuntu-latest - timeout-minutes: 40 - permissions: - contents: write - steps: - - name: Checkout exact PR branch - uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 - with: - ref: agent/event-intelligence-status-gates - fetch-depth: 0 - persist-credentials: true - - - name: Merge current protected main - run: | - git fetch origin main - git merge --no-edit -X theirs origin/main - - - name: Install pinned Rust toolchain - run: rustup toolchain install 1.97.1 --profile minimal --component clippy --component rustfmt - - - name: Add independent-truth regression contract - run: python3 scripts/repair_pr50_add_known_identity_tests.py - - - name: Prove the old detector claim is RED - run: | - set +e - output=$(cargo +1.97.1 test -p event_core --test intelligence_status_contract 2>&1) - status=$? - set -e - printf '%s\n' "$output" - if [ "$status" -eq 0 ]; then - echo "Expected the old API to lack the known-identity baseline boundary" >&2 - exit 1 - fi - grep -E "KnownIdentityStoryDecision|classify_known_identity_baseline" <<<"$output" - - - name: Apply status-gate and baseline implementation - run: | - python3 scripts/repair_pr50_apply_known_identity.py - cargo +1.97.1 fmt --all - - - name: Verify focused and workspace contracts - run: | - cargo +1.97.1 fmt --all --check - cargo +1.97.1 test -p event_core --all-features - cargo +1.97.1 clippy -p event_core --all-targets --all-features -- -D warnings - cargo +1.97.1 test --workspace --all-features - python3 scripts/check_workspace_contract.py - python3 scripts/check_docstrings.py - python3 scripts/validate_documentation.py - - - name: Commit verified repair and remove one-shot files - run: | - rm -f .github/workflows/repair-pr50-known-identity-baseline.yml - rm -f scripts/repair_pr50_add_known_identity_tests.py - rm -f scripts/repair_pr50_apply_known_identity.py - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add -A - git diff --cached --check - git commit -m "fix(event): label known-identity logic as an oracle baseline" - git push origin HEAD:agent/event-intelligence-status-gates diff --git a/scripts/repair_pr50_add_known_identity_tests.py b/scripts/repair_pr50_add_known_identity_tests.py deleted file mode 100644 index ada2651b..00000000 --- a/scripts/repair_pr50_add_known_identity_tests.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Add the PR 50 known-identity baseline regression before implementation.""" - -from pathlib import Path - - -CONTRACT = r'''//! Event-intelligence status gates and an explicitly oracle-assisted baseline. - -use event_core::{ - EventError, EventEvidenceLayer, KnownIdentityStoryDecision, admit_state_transition, - classify_known_identity_baseline, first_story_detection_rates, -}; - -#[test] -fn only_promoted_transitions_enter_the_state_graph() { - admit_state_transition(EventEvidenceLayer::PromotedTransition).expect("promoted"); - assert!(EventEvidenceLayer::PromotedTransition.may_admit_state_transition()); - - assert_eq!( - admit_state_transition(EventEvidenceLayer::TdtDetection), - Err(EventError::DetectionIsNotTransition) - ); - assert_eq!( - admit_state_transition(EventEvidenceLayer::ObservedMention), - Err(EventError::DetectionIsNotTransition) - ); - assert_eq!( - admit_state_transition(EventEvidenceLayer::ChronosPrediction), - Err(EventError::PredictionIsNotFact) - ); - assert_eq!( - admit_state_transition(EventEvidenceLayer::TemporalConsistency), - Err(EventError::DetectionIsNotTransition) - ); - - for layer in [ - EventEvidenceLayer::ObservedMention, - EventEvidenceLayer::TdtDetection, - EventEvidenceLayer::ChronosPrediction, - EventEvidenceLayer::TemporalConsistency, - EventEvidenceLayer::PromotedTransition, - ] { - assert!(!layer.wire_name().is_empty()); - } -} - -#[test] -fn known_identity_baseline_is_scored_against_an_independent_truth_fixture() { - // This is an oracle-assisted identity baseline, not a raw-text first-story detector. - let stream = [10_u64, 20, 10, 30, 20]; - let truth_is_first = [true, true, false, true, false]; - let mut seen = Vec::new(); - let mut predicted_is_first = Vec::new(); - - for story_identity in stream { - let decision = classify_known_identity_baseline(&seen, story_identity); - predicted_is_first.push(matches!( - decision, - KnownIdentityStoryDecision::FirstOccurrence - )); - if matches!(decision, KnownIdentityStoryDecision::FirstOccurrence) { - seen.push(story_identity); - } - } - - let rates = first_story_detection_rates(&truth_is_first, &predicted_is_first) - .expect("independent truth fixture"); - assert_eq!(rates.hits(), 3); - assert_eq!(rates.misses(), 0); - assert_eq!(rates.false_alarms(), 0); - assert!(rates.miss_rate() < 1e-15); - assert!(rates.false_alarm_rate() < 1e-15); - - let always_first = [true, true, true, true, true]; - let false_alarm_rates = first_story_detection_rates(&truth_is_first, &always_first) - .expect("false-alarm fixture"); - assert_eq!(false_alarm_rates.false_alarms(), 2); - assert!((false_alarm_rates.false_alarm_rate() - 1.0).abs() < 1e-15); - - let always_continuation = [false, false, false, false, false]; - let miss_rates = first_story_detection_rates(&truth_is_first, &always_continuation) - .expect("miss fixture"); - assert_eq!(miss_rates.misses(), 3); - assert!((miss_rates.miss_rate() - 1.0).abs() < 1e-15); - assert!(miss_rates.false_alarm_rate() < 1e-15); -} - -#[test] -fn baseline_and_rate_contracts_fail_closed_at_their_boundaries() { - assert_eq!( - classify_known_identity_baseline(&[7], 7), - KnownIdentityStoryDecision::RepeatedIdentity - ); - assert_eq!( - classify_known_identity_baseline(&[7], 8), - KnownIdentityStoryDecision::FirstOccurrence - ); - assert_eq!( - first_story_detection_rates(&[], &[]), - Err(EventError::InvalidWirePayload) - ); - assert_eq!( - first_story_detection_rates(&[true], &[true, false]), - Err(EventError::InvalidWirePayload) - ); - - let all_continuations = first_story_detection_rates(&[false, false], &[false, false]) - .expect("zero first-story denominator"); - assert!(all_continuations.miss_rate() < 1e-15); - let all_first = first_story_detection_rates(&[true, true], &[true, false]) - .expect("zero continuation denominator"); - assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); - assert!(all_first.false_alarm_rate() < 1e-15); -} -''' - -path = Path("crates/event_core/tests/intelligence_status_contract.rs") -path.parent.mkdir(parents=True, exist_ok=True) -path.write_text(CONTRACT, encoding="utf-8") diff --git a/scripts/repair_pr50_apply_known_identity.py b/scripts/repair_pr50_apply_known_identity.py deleted file mode 100644 index 6342c9c6..00000000 --- a/scripts/repair_pr50_apply_known_identity.py +++ /dev/null @@ -1,398 +0,0 @@ -"""Apply the PR 50 event-status and known-identity baseline repair.""" - -from pathlib import Path - - -def replace_once(path: str, old: str, new: str) -> None: - """Replace one exact fragment or fail closed.""" - file_path = Path(path) - text = file_path.read_text(encoding="utf-8") - count = text.count(old) - if count != 1: - raise SystemExit(f"{path}: expected one replacement target, found {count}") - file_path.write_text(text.replace(old, new, 1), encoding="utf-8") - - -INTELLIGENCE = r'''//! Evidence-status gates and oracle-assisted story-identity baselines. - -use crate::EventError; - -/// Epistemic layer of an event-intelligence output. -/// -/// Only [`EventEvidenceLayer::PromotedTransition`] may enter the forward -/// state/input-process-outcome graph. TDT detections and CHRONOS predictions -/// remain measurement or hypothesis artifacts. -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum EventEvidenceLayer { - /// Fallible textual mention grounded in evidence. - ObservedMention, - /// TDT-style detection, link, or track output. - TdtDetection, - /// CHRONOS-style schema completion or predicted event. - ChronosPrediction, - /// Symbolic temporal-consistency judgment. - TemporalConsistency, - /// Independently promoted forward state transition. - PromotedTransition, -} - -impl EventEvidenceLayer { - /// Stable wire name for this layer. - #[must_use] - pub const fn wire_name(self) -> &'static str { - match self { - Self::ObservedMention => "observed_mention", - Self::TdtDetection => "tdt_detection", - Self::ChronosPrediction => "chronos_prediction", - Self::TemporalConsistency => "temporal_consistency", - Self::PromotedTransition => "promoted_transition", - } - } - - /// Whether this layer may admit a forward state-transition edge. - #[must_use] - pub const fn may_admit_state_transition(self) -> bool { - matches!(self, Self::PromotedTransition) - } -} - -/// Admit a layer into the forward state graph or fail closed. -/// -/// # Errors -/// -/// Returns [`EventError::PredictionIsNotFact`] for CHRONOS predictions and -/// [`EventError::DetectionIsNotTransition`] for every other non-promoted layer. -pub fn admit_state_transition(layer: EventEvidenceLayer) -> Result<(), EventError> { - if layer.may_admit_state_transition() { - Ok(()) - } else if matches!(layer, EventEvidenceLayer::ChronosPrediction) { - Err(EventError::PredictionIsNotFact) - } else { - Err(EventError::DetectionIsNotTransition) - } -} - -/// Oracle-assisted identity-baseline decision for one candidate story. -/// -/// This baseline assumes a stable gold or externally adjudicated story identity. -/// It is not a detector over raw text, embeddings, or document metadata. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum KnownIdentityStoryDecision { - /// The externally supplied story identity has not appeared before. - FirstOccurrence, - /// The externally supplied story identity already appeared in the stream. - RepeatedIdentity, -} - -/// Classify one externally identified story against identities already observed. -/// -/// This function is an oracle-assisted baseline for scoring and regression -/// tests. Product code must not present it as first-story detection from raw -/// documents. -#[must_use] -pub fn classify_known_identity_baseline( - seen_story_ids: &[u64], - candidate_story_id: u64, -) -> KnownIdentityStoryDecision { - if seen_story_ids.contains(&candidate_story_id) { - KnownIdentityStoryDecision::RepeatedIdentity - } else { - KnownIdentityStoryDecision::FirstOccurrence - } -} - -/// Known-truth first-story detection counts. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct FirstStoryRates { - hits: usize, - misses: usize, - false_alarms: usize, - first_story_truth: usize, - continuation_truth: usize, -} - -impl FirstStoryRates { - /// Correct first-story detections. - #[must_use] - pub const fn hits(self) -> usize { - self.hits - } - - /// Missed first stories. - #[must_use] - pub const fn misses(self) -> usize { - self.misses - } - - /// Continuations labeled as first stories. - #[must_use] - pub const fn false_alarms(self) -> usize { - self.false_alarms - } - - /// Miss rate among true first stories. - #[must_use] - pub fn miss_rate(self) -> f64 { - if self.first_story_truth == 0 { - 0.0 - } else { - #[allow(clippy::cast_precision_loss)] - { - self.misses as f64 / self.first_story_truth as f64 - } - } - } - - /// False-alarm rate among true continuations. - #[must_use] - pub fn false_alarm_rate(self) -> f64 { - if self.continuation_truth == 0 { - 0.0 - } else { - #[allow(clippy::cast_precision_loss)] - { - self.false_alarms as f64 / self.continuation_truth as f64 - } - } - } -} - -/// Score predicted first-story labels against an independently supplied truth. -/// -/// # Errors -/// -/// Returns [`EventError::InvalidWirePayload`] when the streams are empty or -/// have unequal length. -pub fn first_story_detection_rates( - truth_is_first: &[bool], - predicted_is_first: &[bool], -) -> Result { - if truth_is_first.is_empty() || truth_is_first.len() != predicted_is_first.len() { - return Err(EventError::InvalidWirePayload); - } - let mut hits = 0; - let mut misses = 0; - let mut false_alarms = 0; - let mut first_story_truth = 0; - let mut continuation_truth = 0; - for (&truth, &predicted) in truth_is_first.iter().zip(predicted_is_first) { - if truth { - first_story_truth += 1; - if predicted { - hits += 1; - } else { - misses += 1; - } - } else { - continuation_truth += 1; - if predicted { - false_alarms += 1; - } - } - } - Ok(FirstStoryRates { - hits, - misses, - false_alarms, - first_story_truth, - continuation_truth, - }) -} - -#[cfg(test)] -mod tests { - use super::{ - EventEvidenceLayer, FirstStoryRates, KnownIdentityStoryDecision, - classify_known_identity_baseline, first_story_detection_rates, - }; - - #[test] - fn zero_denominator_rates_are_zero_and_identity_baseline_is_explicit() { - assert_eq!( - classify_known_identity_baseline(&[7], 7), - KnownIdentityStoryDecision::RepeatedIdentity - ); - assert_eq!( - classify_known_identity_baseline(&[7], 8), - KnownIdentityStoryDecision::FirstOccurrence - ); - let empty_classes = FirstStoryRates { - hits: 0, - misses: 0, - false_alarms: 0, - first_story_truth: 0, - continuation_truth: 0, - }; - assert!(empty_classes.miss_rate() < 1e-15); - assert!(empty_classes.false_alarm_rate() < 1e-15); - let all_first = first_story_detection_rates(&[true, true], &[true, false]).expect("all"); - assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); - assert!(all_first.false_alarm_rate() < 1e-15); - assert_eq!( - EventEvidenceLayer::TdtDetection.wire_name(), - "tdt_detection" - ); - } -} -''' - -Path("crates/event_core/src/intelligence.rs").write_text(INTELLIGENCE, encoding="utf-8") - -replace_once( - "crates/event_core/src/error.rs", - """ /// An unknown event-role name was supplied. - UnknownEventRole, -""", - """ /// An unknown event-role name was supplied. - UnknownEventRole, - /// A TDT detection or mention was treated as a state transition. - DetectionIsNotTransition, - /// A CHRONOS prediction was treated as an observed or promoted fact. - PredictionIsNotFact, -""", -) -replace_once( - "crates/event_core/src/error.rs", - """ Self::UnknownEventRole => \"unknown event role\", -""", - """ Self::UnknownEventRole => \"unknown event role\", - Self::DetectionIsNotTransition => \"detection is not a state transition\", - Self::PredictionIsNotFact => \"prediction is not an observed fact\", -""", -) -replace_once( - "crates/event_core/src/error.rs", - """ (EventError::UnknownEventRole, \"unknown event role\"), -""", - """ (EventError::UnknownEventRole, \"unknown event role\"), - ( - EventError::DetectionIsNotTransition, - \"detection is not a state transition\", - ), - ( - EventError::PredictionIsNotFact, - \"prediction is not an observed fact\", - ), -""", -) - -replace_once( - "crates/event_core/src/lib.rs", - """mod identifier; -mod instance; -""", - """mod identifier; -mod intelligence; -mod instance; -""", -) -replace_once( - "crates/event_core/src/lib.rs", - """/// Opaque event-instance identifier. -pub use identifier::EventInstanceId; -""", - """/// Opaque event-instance identifier. -pub use identifier::EventInstanceId; -/// Admit only independently promoted state transitions. -pub use intelligence::admit_state_transition; -/// Oracle-assisted classification using externally supplied story identities. -pub use intelligence::classify_known_identity_baseline; -/// Score first-story predictions against independent known truth. -pub use intelligence::first_story_detection_rates; -/// Epistemic layer for event-intelligence output. -pub use intelligence::EventEvidenceLayer; -/// First-story miss and false-alarm summary. -pub use intelligence::FirstStoryRates; -/// Decision from the oracle-assisted known-identity baseline. -pub use intelligence::KnownIdentityStoryDecision; -""", -) - -RESEARCH = r'''# Event-intelligence status gates and known-identity baseline - -## Scope - -This note doctors the first ADR 0016 production slice in `event_core`: - -1. every event-intelligence output carries an epistemic layer (`observed_mention`, `tdt_detection`, `chronos_prediction`, `temporal_consistency`, `promoted_transition`); -2. only an independently promoted transition may enter the forward state graph; -3. CHRONOS predictions are never treated as observed fact; -4. generic first-story miss and false-alarm rates are scored against an independently supplied truth vector; -5. the committed story-identity classifier is explicitly an oracle-assisted known-identity baseline, not a detector over raw documents. - -Full raw-text TDT detection, linking, tracking, calibration, and CHRONOS schema extraction remain accepted-target. No database migration is allocated. - -## Authoritative sources - -Allan, J. (Ed.). (2002). *Topic detection and tracking: Event-based information organization*. Kluwer Academic Publishers. - -Anagnostopoulos, E., Batsakis, S., & Petrakis, E. G. M. (2013). CHRONOS: A reasoning engine for qualitative temporal information in OWL. *Procedia Computer Science, 22*, 70–77. https://doi.org/10.1016/j.procs.2013.09.082 - -## Application - -Allan (2002) defines first-story detection as a scored measurement task with miss and false-alarm rates, not as automatic promotion into a chronology. A repeated externally supplied story identifier can provide a deterministic oracle baseline for regression testing, but it cannot establish detection performance from text because the identity already contains the answer. Anagnostopoulos et al. (2013) keep qualitative temporal reasoning distinct from asserted event identity. TEPP therefore refuses to admit TDT detections or CHRONOS predictions as state transitions, exposes generic rate scoring against independent truth, and labels identity-membership logic as a baseline rather than a detector. - -## Verification - -- `admit_state_transition(PromotedTransition)` succeeds; -- TDT/mention/consistency layers return `DetectionIsNotTransition`; -- CHRONOS predictions return `PredictionIsNotFact`; -- the identity stream `[10,20,10,30,20]` is scored against the independently fixed truth vector `[true,true,false,true,false]`; -- always-first and always-continuation predictions exercise false-alarm and miss paths; -- empty and unequal truth/prediction vectors fail closed; -- no product or scientific claim treats the known-identity baseline as raw-text first-story detection. -''' -Path("docs/research/event-intelligence-status-gates.md").write_text(RESEARCH, encoding="utf-8") - -changelog_path = Path("CHANGELOG.md") -changelog = changelog_path.read_text(encoding="utf-8") -bullet = "- `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; generic first-story miss/false-alarm scoring is paired with an explicitly oracle-assisted known-identity baseline.\n" -if bullet not in changelog: - marker = "### Added\n\n" - if changelog.count(marker) != 1: - raise SystemExit("CHANGELOG Added marker mismatch") - changelog = changelog.replace(marker, marker + bullet, 1) -changelog_path.write_text(changelog, encoding="utf-8") - -replace_once( - "DOCUMENTATION.md", - """| Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | -""", - """| Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | -| Event-intelligence status-gate doctoring | [`docs/research/event-intelligence-status-gates.md`](docs/research/event-intelligence-status-gates.md) | -""", -) -replace_once( - "docs/TRACEABILITY.md", - """| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; full intelligence stack remaining | partial | -""", - """| event ontology/evidence mentions | PRD; ADR 0003 | `event_core` mention/instance separation on protected main; ADR 0016 evidence-status gates on the active PR | partial | -""", -) -replace_once( - "docs/TRACEABILITY.md", - """| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | -""", - """| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` admission gates, generic rates, and a known-identity baseline on the active PR; raw-text TDT/CHRONOS stack remaining | partial | -""", -) -replace_once( - "docs/adr/0016-tdt-chronos-event-intelligence-boundary.md", - "**Implementation maturity:** accepted-target \n", - "**Implementation maturity:** partial — evidence-layer admission gates, generic first-story rate scoring, and an oracle-assisted known-identity baseline are implemented on the active PR; raw-text TDT tracking/calibration and CHRONOS schema extraction remain accepted-target\n", -) -replace_once( - "docs/adr/README.md", - """| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | accepted-target | Separates observed evidence, detection/tracking, prediction/schema inference, temporal consistency, and promoted transition authority. | -""", - """| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | partial | Admission gates, generic first-story rates, and a known-identity baseline are on the active PR; raw-text TDT/CHRONOS work remains accepted-target. | -""", -) -replace_once( - "docs/validation/temporal-event-foundation.md", - """| Versioned API/export contracts | `tepp_api` | implemented-main | — | unknown-field/version/limit tests | Task 12 / PR #21; HTTP service remaining | -""", - """| Versioned API/export contracts | `tepp_api` | implemented-main | — | unknown-field/version/limit tests | Task 12 / PR #21; HTTP service remaining | -| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + generic first-story rates | independent truth + known-identity baseline | ADR 0016; `docs/research/event-intelligence-status-gates.md` | -""", -) From af67f14be8fbe4950fd3388f168b47d5158af55f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 17:37:53 +0900 Subject: [PATCH 12/19] docs(adr): align event intelligence maturity --- docs/adr/0016-tdt-chronos-event-intelligence-boundary.md | 2 +- docs/adr/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md index b85ee0b4..45731fdb 100644 --- a/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md +++ b/docs/adr/0016-tdt-chronos-event-intelligence-boundary.md @@ -1,7 +1,7 @@ # ADR 0016 — TDT, CHRONOS, and Event Ontology intelligence boundary **Decision status:** Accepted -**Implementation maturity:** accepted-target +**Implementation maturity:** active-PR — evidence-layer admission and first-story detection rates are implemented in `event_core` on the active PR; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. **Date:** 2026-08-12 **Supersedes:** None; complements ADR 0002 temporal semantics and ADR 0003 event ontology/membership. diff --git a/docs/adr/README.md b/docs/adr/README.md index 5f7fce3d..bbc318f9 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -21,7 +21,7 @@ Read [`ADR_POLICY.md`](ADR_POLICY.md) first. **Decision status and implementatio | [0013](0013-bitemporal-persistence-reproducibility-and-split-authority.md) | Bitemporal persistence, reproducibility manifests, and relation-aware split authority | Accepted | partial | Owns PostgreSQL adapter semantics, immutable run/split manifests, leakage-safe partitions, and recovery identity; optional `live-sqlx` `PgPool`, live PG CI, tenant RLS, and `0006` membership implemented-main; `0007` retention/deletion/legal-hold on the active PR; remaining physical ERD/backup accepted-target. | | [0014](0014-scientific-claim-promotion-and-release-evidence.md) | Scientific claim promotion and release evidence authority | Accepted | partial | Separates design, implementation, scientific/product claim, and release authority; repository SBOM/provenance generator implemented, full release bundle remaining. | | [0015](0015-autonomous-development-review-and-merge-authority.md) | Autonomous development, review, and merge authority separation | Accepted | active-PR | Separates model proposal, deterministic verification, publication, independent review, and merge/release authority. | -| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | partial | Evidence-layer admission and first-story rates are on the active PR; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. | +| [0016](0016-tdt-chronos-event-intelligence-boundary.md) | TDT, CHRONOS, and Event Ontology intelligence boundary | Accepted | active-PR | Evidence-layer admission and first-story rates are on the active PR; full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. | ## Decision ownership summary From fa4dff21b6cbb809c50e8f8efe6bf09ff15cb4ba Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 18:24:22 +0900 Subject: [PATCH 13/19] docs: align provider payload maturity evidence --- docs/TRACEABILITY.md | 2 +- docs/validation/temporal-event-foundation.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 67ba69f7..7e3e30ab 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -33,7 +33,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | | evidence-bounded LLM interpretation | ADR 0010/0012; PRD | `tepp_api` router plus future `interpretation_gateway` | partial | | adaptive direct/verify/committee/conductor test-time compute | ADR 0010; `docs/LLM_ORCHESTRATION.md` | `tepp_api::route_orchestration` + ablation record on the active PR; live contextual-orchestrator execution remaining | partial | -| purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | `tepp_api` export authorization plus provider-payload minimization / elevated re-identification implemented-main; persistence retention/deletion remaining | partial | +| purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | `tepp_api` export authorization and elevated re-identification are implemented-main; provider-payload minimization remains active-PR; migration `0007` retention/deletion/legal-hold SQL contracts are implemented-main; deployment/provider evidence remains accepted-target | partial | | tenant/purpose/role/lifetime access and identity separation | ADR 0009; Threat Model | `tepp_api` time-bounded `PurposeGrant` + cross-tenant denial implemented-main; persistent `access_grant` storage remaining | partial | | standalone + modular CWL MSA / no cross-service DB coupling | ADR 0011; `docs/API_CONTRACT.md` | current standalone crates; future service ports | partial | | naruon modular artifact consumer boundary | ADR 0011/0012; API contract | `docs/connectors/naruon-artifact-consumer.md` + PR #22 versioned consumer contract on protected main; HTTP service remaining | partial | diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index 71da0d82..f470dc7e 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -24,7 +24,7 @@ This report tracks exact-head scientific and engineering evidence required befor | Recovery metrics | `validation_core` | implemented-main | — | RMSE/bias/coverage/MC gates | Task 11 / PR #19 | | 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 | | TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + first-story rates | known-stream miss/FA | ADR 0016; `docs/research/event-intelligence-status-gates.md` | - | Purpose-bound provider payloads | `tepp_api` | implemented-main | provider-payload minimization | expired/not-yet-valid/inverted/cross-tenant/impossible-calendar grant, mapping refusal, audited elevated re-id replay | ADR 0009; `docs/research/provider-payload-minimization.md` | + | Purpose-bound provider payloads | `tepp_api` | accepted-target | active PR | expired/not-yet-valid/inverted/cross-tenant/impossible-calendar grant, mapping refusal, audited elevated re-id replay | ADR 0009; `docs/research/provider-payload-minimization.md` | | Adaptive orchestration router | `tepp_api` | accepted-target | active PR | mode selection, document-control denial, ablation, credential-free bind | ADR 0010; `docs/research/adaptive-orchestration-router.md` | | CWL modular connectors | `docs/connectors/*` | implemented-main | — | contract docs + examples | PR #22; live HTTP ports remaining | | Release SBOM/provenance generator | `scripts/release_evidence.py` | partial | — | generate+validate in CI | Task 13 partial / PR #28 | From 2a29e2496409b5ab411eed5f045f89d52a24abcf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 20:00:24 +0900 Subject: [PATCH 14/19] test(event): cover every intelligence branch --- crates/event_core/src/intelligence.rs | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/crates/event_core/src/intelligence.rs b/crates/event_core/src/intelligence.rs index 04f901ff..f9596698 100644 --- a/crates/event_core/src/intelligence.rs +++ b/crates/event_core/src/intelligence.rs @@ -177,25 +177,31 @@ pub fn first_story_detection_rates( #[cfg(test)] mod tests { use super::{ - EventEvidenceLayer, FirstStoryRates, TdtStoryDecision, classify_tdt_story, - first_story_detection_rates, + EventEvidenceLayer, TdtStoryDecision, classify_tdt_story, first_story_detection_rates, }; #[test] fn zero_denominator_rates_are_zero_and_track_is_not_first() { assert_eq!(classify_tdt_story(&[7], 7), TdtStoryDecision::Track); - let empty_classes = FirstStoryRates { - hits: 0, - misses: 0, - false_alarms: 0, - first_story_truth: 0, - continuation_truth: 0, - }; - assert!(empty_classes.miss_rate() < 1e-15); - assert!(empty_classes.false_alarm_rate() < 1e-15); + assert_eq!(classify_tdt_story(&[], 8), TdtStoryDecision::FirstStory); + assert!(first_story_detection_rates(&[], &[]).is_err()); + assert!(first_story_detection_rates(&[true], &[true, false]).is_err()); + let no_first_story = std::hint::black_box( + first_story_detection_rates(&[false], &[false]).expect("no first"), + ); + assert!(no_first_story.miss_rate() < 1e-15); + let no_continuation = + std::hint::black_box(first_story_detection_rates(&[true], &[true]).expect("no track")); + assert!(no_continuation.false_alarm_rate() < 1e-15); let all_first = first_story_detection_rates(&[true, true], &[true, false]).expect("all"); assert!((all_first.miss_rate() - 0.5).abs() < 1e-15); assert!(all_first.false_alarm_rate() < 1e-15); + let continuations = + first_story_detection_rates(&[false, false], &[true, false]).expect("continuations"); + assert_eq!(continuations.false_alarms(), 1); + assert_eq!(continuations.misses(), 0); + assert!(continuations.miss_rate() < 1e-15); + assert!((continuations.false_alarm_rate() - 0.5).abs() < 1e-15); assert_eq!( EventEvidenceLayer::TdtDetection.wire_name(), "tdt_detection" From 1b12210af301098a18ad0cf729b947f20db02a2e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 00:16:48 +0900 Subject: [PATCH 15/19] docs: align event intelligence maturity evidence --- DOCUMENTATION.md | 8 ++++---- docs/TRACEABILITY.md | 12 ++++++------ docs/adr/0009-purpose-bound-pii-governance.md | 2 +- docs/adr/0010-adaptive-llm-orchestration.md | 2 +- docs/adr/README.md | 2 +- docs/validation/temporal-event-foundation.md | 12 ++++++------ 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 265ae164..d16a2050 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -33,10 +33,10 @@ TEPP's approved PRD v0.4 and implementation plan are the primary product baselin | Hourly NIM product-development operations | [`docs/operations/HOURLY_NIM_PRODUCT_DEVELOPMENT.md`](docs/operations/HOURLY_NIM_PRODUCT_DEVELOPMENT.md) | | Actions workflow fleet audit | [`docs/operations/ACTIONS_WORKFLOW_FLEET.md`](docs/operations/ACTIONS_WORKFLOW_FLEET.md) | | Actions fleet research doctoring | [`docs/research/actions-workflow-fleet.md`](docs/research/actions-workflow-fleet.md) | - | Event-intelligence status-gate doctoring | [`docs/research/event-intelligence-status-gates.md`](docs/research/event-intelligence-status-gates.md) | - | Retention/deletion/legal-hold doctoring | [`docs/research/retention-deletion-legal-hold.md`](docs/research/retention-deletion-legal-hold.md) | - | Provider-payload minimization doctoring | [`docs/research/provider-payload-minimization.md`](docs/research/provider-payload-minimization.md) | - | Adaptive orchestration router doctoring | [`docs/research/adaptive-orchestration-router.md`](docs/research/adaptive-orchestration-router.md) | +| Event-intelligence status-gate doctoring | [`docs/research/event-intelligence-status-gates.md`](docs/research/event-intelligence-status-gates.md) | +| Retention/deletion/legal-hold doctoring | [`docs/research/retention-deletion-legal-hold.md`](docs/research/retention-deletion-legal-hold.md) | +| Provider-payload minimization doctoring | [`docs/research/provider-payload-minimization.md`](docs/research/provider-payload-minimization.md) | +| Adaptive orchestration router doctoring | [`docs/research/adaptive-orchestration-router.md`](docs/research/adaptive-orchestration-router.md) | | Hourly NIM OpenCode doctoring | [`docs/doctoring/hourly-nim-opencode-development.md`](docs/doctoring/hourly-nim-opencode-development.md) | | Change history | [`CHANGELOG.md`](CHANGELOG.md) | diff --git a/docs/TRACEABILITY.md b/docs/TRACEABILITY.md index 7e3e30ab..daa6655e 100644 --- a/docs/TRACEABILITY.md +++ b/docs/TRACEABILITY.md @@ -1,7 +1,7 @@ # TEPP Requirements, Research, and Evidence Traceability **Status:** Accepted cross-cutting traceability baseline -**Last reviewed:** 2026-08-13 +**Last reviewed:** 2026-08-20 The full APA 7th standards/literature register remains `docs/research/standards-and-literature.md`. This matrix links durable requirements to their owning decisions and implementation/evidence maturity without duplicating the bibliography. @@ -17,7 +17,7 @@ The full APA 7th standards/literature register remains `docs/research/standards- | 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 | | 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` on active PR); remaining physical ERD/backup remaining | partial | +| 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 (`#44` implemented-main); 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 | | versioned service/API contracts and exports | PRD; API contract; ADR 0011/0013 | `tepp_api` analysis-run/export/JSON-LD/GraphML contracts on protected main (PR #21); HTTP service remaining accepted-target | partial | | immutable split/run/reproducibility manifests | ADR 0013; ERD | `tepp_api` reproducibility manifest contract on protected main; `persistence_postgres` append-only SQL insert/lookup for `reproducibility_manifest`, `corpus_split_manifest`, `model_run`, and `model_artifact` (migration `0003`); full physical ERD constraints remaining | partial | @@ -30,13 +30,13 @@ The full APA 7th standards/literature register remains `docs/research/standards- | compositional topic correlation / stable clustering | ADR 0005/0012; research | future `network_analysis` | accepted-target | | posterior ESEM / longitudinal invariance / DSEM | ADR 0005 | future `psychometric_core` | accepted-target | | CPU bounded multithreading + GPU/VRAM streaming/parity | ADR 0001/0006 | future `compute_backend` | accepted-target | -| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | future `event_intelligence` | accepted-target | +| TDT detection/tracking vs CHRONOS schema/prediction/temporal consistency | ADR 0016; PRD/research | `event_core` evidence-layer admission and known-stream first-story rates on PR #50; full tracking/calibration and schema extraction remain future | active-PR | | evidence-bounded LLM interpretation | ADR 0010/0012; PRD | `tepp_api` router plus future `interpretation_gateway` | partial | -| adaptive direct/verify/committee/conductor test-time compute | ADR 0010; `docs/LLM_ORCHESTRATION.md` | `tepp_api::route_orchestration` + ablation record on the active PR; live contextual-orchestrator execution remaining | partial | -| purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | `tepp_api` export authorization and elevated re-identification are implemented-main; provider-payload minimization remains active-PR; migration `0007` retention/deletion/legal-hold SQL contracts are implemented-main; deployment/provider evidence remains accepted-target | partial | +| adaptive direct/verify/committee/conductor test-time compute | ADR 0010; `docs/LLM_ORCHESTRATION.md` | `tepp_api::route_orchestration`, ablation record, and credential-free contextual-orchestrator binding on protected main; live execution and learned conductor calibration remain future | partial | +| purpose-bound PII handling without blanket masking | ADR 0009; `docs/PRIVACY_DATA_GOVERNANCE.md` | `tepp_api` export authorization, elevated re-identification, and provider-payload minimization are implemented-main; migration `0007` retention/deletion/legal-hold SQL contracts are implemented-main; deployment/provider evidence remains accepted-target | partial | | tenant/purpose/role/lifetime access and identity separation | ADR 0009; Threat Model | `tepp_api` time-bounded `PurposeGrant` + cross-tenant denial implemented-main; persistent `access_grant` storage remaining | partial | | standalone + modular CWL MSA / no cross-service DB coupling | ADR 0011; `docs/API_CONTRACT.md` | current standalone crates; future service ports | partial | -| naruon modular artifact consumer boundary | ADR 0011/0012; API contract | `docs/connectors/naruon-artifact-consumer.md` + PR #22 versioned consumer contract on protected main; HTTP service remaining | partial | +| naruon modular artifact consumer boundary | ADR 0011/0012; API contract | `docs/connectors/naruon-artifact-consumer.md` + PR #22 versioned consumer contract and `tepp_api` HTTP interchange on protected main; live HTTP service remaining | partial | | contextual-orchestrator interpretation port boundary | ADR 0010/0011; LLM orchestration | `docs/connectors/contextual-orchestrator-interpretation-port.md`; live port remaining | partial | | Actions registry identities bound to protected-main tree (orphan disable) | Operability; GitHub Actions REST | `scripts/actions_workflow_fleet.py` + issue #20 tests/doctoring; live disable remains operator-authorized | active-PR | | autonomous model proposal separated from verification/publication/review/merge | ADR 0015 | future safe OpenCode/NVIDIA autonomous-development workflow | accepted-target | diff --git a/docs/adr/0009-purpose-bound-pii-governance.md b/docs/adr/0009-purpose-bound-pii-governance.md index 88ed1341..e06de6e1 100644 --- a/docs/adr/0009-purpose-bound-pii-governance.md +++ b/docs/adr/0009-purpose-bound-pii-governance.md @@ -1,7 +1,7 @@ # ADR 0009 — Purpose-bound PII governance without blanket masking **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 +**Implementation maturity:** partial — persistence retention/deletion/legal-hold (migration `0007`) and purpose-bound provider-payload minimization (expired-purpose denial, log/source separation, separately authorized re-identification) are implemented-main; deployment/provider-region evidence remains accepted-target **Date:** 2026-08-10 **Supersedes:** None. diff --git a/docs/adr/0010-adaptive-llm-orchestration.md b/docs/adr/0010-adaptive-llm-orchestration.md index a3378983..b8a0e17f 100644 --- a/docs/adr/0010-adaptive-llm-orchestration.md +++ b/docs/adr/0010-adaptive-llm-orchestration.md @@ -1,7 +1,7 @@ # ADR 0010 — Adaptive LLM orchestration and test-time compute **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 +**Implementation maturity:** partial — `tepp_api` governed router, comparable-budget ablation record, and credential-free contextual-orchestrator binding are implemented-main; live NIM execution, learned conductor calibration, and production ablation evidence remain accepted-target **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. diff --git a/docs/adr/README.md b/docs/adr/README.md index bbc318f9..42c50b32 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -15,7 +15,7 @@ Read [`ADR_POLICY.md`](ADR_POLICY.md) first. **Decision status and implementatio | [0007](0007-rust-workspace-quality-gates.md) | Explicit Rust workspace, pinned toolchains, and exact quality gates | Accepted | implemented-main | ADR 0014 governs scientific/product claim promotion beyond repository-quality tooling. | | [0008](0008-immutable-evidence-identities-digests-and-spans.md) | Immutable evidence identities, `SHA-256` digests, exact spans, and strict wire reconstruction | Accepted | implemented-main | ADR 0013 governs future persistence/reproducibility/split authority. | | [0009](0009-purpose-bound-pii-governance.md) | Purpose-bound PII governance without blanket masking | Accepted | partial | Persistence retention/deletion/legal-hold (`0007`) and provider-payload minimization implemented-main; deployment evidence remains accepted-target. | -| [0010](0010-adaptive-llm-orchestration.md) | Adaptive LLM orchestration and test-time compute | Accepted | partial | `tepp_api` router/ablation/orchestrator binding on the active PR; live NIM execution and production ablation evidence remain accepted-target. | +| [0010](0010-adaptive-llm-orchestration.md) | Adaptive LLM orchestration and test-time compute | Accepted | partial | `tepp_api` router/ablation/orchestrator binding implemented-main; live NIM execution and production ablation evidence remain accepted-target. | | [0011](0011-standalone-modular-msa-boundary.md) | Standalone operation and modular CWL MSA boundary | Accepted | partial | Owns cross-service persistence/credential/API authority; no direct cross-service application-table coupling. | | [0012](0012-temporal-relational-shared-latent-topic-measurement.md) | Temporal Relational Shared-Latent Topic Measurement (TRSL-TM) | Accepted | accepted-target | Owns topic backend compatibility, global topic identity, method effects, K/model-selection prerequisites, and compositional topic coordinates. | | [0013](0013-bitemporal-persistence-reproducibility-and-split-authority.md) | Bitemporal persistence, reproducibility manifests, and relation-aware split authority | Accepted | partial | Owns PostgreSQL adapter semantics, immutable run/split manifests, leakage-safe partitions, and recovery identity; optional `live-sqlx` `PgPool`, live PG CI, tenant RLS, and `0006` membership implemented-main; `0007` retention/deletion/legal-hold on the active PR; remaining physical ERD/backup accepted-target. | diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index f470dc7e..865a6661 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -1,7 +1,7 @@ # Temporal Event Foundation — validation and release-readiness report **Status:** Living validation ledger for the Temporal/Event foundation program -**Last reviewed:** 2026-08-12 +**Last reviewed:** 2026-08-20 **Authority:** ADR 0014 (claim promotion), ADR 0007 (quality gates), AGENTS.md scientific acceptance ## Scope @@ -18,14 +18,14 @@ 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 | — | unit + ESS weights | Task 7 / PR #12 + #25 | | Forward transition DAG | `relation_graph` | implemented-main | — | unit + cycle rejection | Task 6 / PR #14 | -| Bitemporal persistence + live SQL port | `persistence_postgres` | partial | #36 typed membership | migration contracts + recording transport + optional PgPool + live CI + tenant RLS + `0005` interval CHECKs (implemented-main via #35) + `0006` typed membership (active PR) | Task 8 / PR #16 + #23 + #26 + #27 + #29 + #30–#35 + `0006` | +| Bitemporal persistence + live SQL port | `persistence_postgres` | partial | — | migration contracts + recording transport + optional PgPool + live CI + tenant RLS + `0005`/`0006` interval and membership contracts + event relation/mention/instance + source-artifact + audit-event + concurrent-write + restore-integrity contracts implemented-main | Task 8 / PR #16 + #23 + #26 + #27 + #29 + #30–#44 | | Leakage-safe splits | `corpus_split` | implemented-main | — | cutoff + co-partition tests | Task 9 / PR #17 | | Truth corpora / manifests | `tepp_simulation` | implemented-main | — | deterministic generator tests | Task 10 / PR #18 | | Recovery metrics | `validation_core` | implemented-main | — | RMSE/bias/coverage/MC gates | Task 11 / PR #19 | - | 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 | - | TDT/CHRONOS evidence-status gates | `event_core` | active-PR | admission + first-story rates | known-stream miss/FA | ADR 0016; `docs/research/event-intelligence-status-gates.md` | - | Purpose-bound provider payloads | `tepp_api` | accepted-target | active PR | expired/not-yet-valid/inverted/cross-tenant/impossible-calendar grant, mapping refusal, audited elevated re-id replay | ADR 0009; `docs/research/provider-payload-minimization.md` | - | Adaptive orchestration router | `tepp_api` | accepted-target | active PR | mode selection, document-control denial, ablation, credential-free bind | ADR 0010; `docs/research/adaptive-orchestration-router.md` | +| 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 | +| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | PR #50 | admission + first-story rates | known-stream miss/FA; full tracking/calibration/schema extraction remains future | ADR 0016; `docs/research/event-intelligence-status-gates.md` | +| Purpose-bound provider payloads | `tepp_api` | implemented-main | — | expired/not-yet-valid/inverted/cross-tenant/impossible-calendar grant, mapping refusal, audited elevated re-id replay | ADR 0009; `docs/research/provider-payload-minimization.md` | +| Adaptive orchestration router | `tepp_api` | partial | — | mode selection, document-control denial, ablation, credential-free bind; live NIM execution remains future | ADR 0010; `docs/research/adaptive-orchestration-router.md` | | CWL modular connectors | `docs/connectors/*` | implemented-main | — | contract docs + examples | PR #22; live HTTP ports remaining | | Release SBOM/provenance generator | `scripts/release_evidence.py` | partial | — | generate+validate in CI | Task 13 partial / PR #28 | From a189b97e536648b856cb38ea2c95270196ce3b52 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 01:22:21 +0900 Subject: [PATCH 16/19] docs: fix temporal ledger table shape --- docs/validation/temporal-event-foundation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/validation/temporal-event-foundation.md b/docs/validation/temporal-event-foundation.md index 865a6661..e9b32b84 100644 --- a/docs/validation/temporal-event-foundation.md +++ b/docs/validation/temporal-event-foundation.md @@ -23,7 +23,7 @@ This report tracks exact-head scientific and engineering evidence required befor | Truth corpora / manifests | `tepp_simulation` | implemented-main | — | deterministic generator tests | Task 10 / PR #18 | | Recovery metrics | `validation_core` | implemented-main | — | RMSE/bias/coverage/MC gates | Task 11 / PR #19 | | 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 | -| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | PR #50 | admission + first-story rates | known-stream miss/FA; full tracking/calibration/schema extraction remains future | ADR 0016; `docs/research/event-intelligence-status-gates.md` | +| TDT/CHRONOS evidence-status gates | `event_core` | active-PR | PR #50 | admission + first-story rates | known-stream miss/FA; full tracking/calibration/schema extraction remains future; ADR 0016; `docs/research/event-intelligence-status-gates.md` | | Purpose-bound provider payloads | `tepp_api` | implemented-main | — | expired/not-yet-valid/inverted/cross-tenant/impossible-calendar grant, mapping refusal, audited elevated re-id replay | ADR 0009; `docs/research/provider-payload-minimization.md` | | Adaptive orchestration router | `tepp_api` | partial | — | mode selection, document-control denial, ablation, credential-free bind; live NIM execution remains future | ADR 0010; `docs/research/adaptive-orchestration-router.md` | | CWL modular connectors | `docs/connectors/*` | implemented-main | — | contract docs + examples | PR #22; live HTTP ports remaining | From 8710e248ad958a65199662b96852e99cd45128df Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 13:09:57 +0900 Subject: [PATCH 17/19] fix(event): enforce evidence layer at promotion boundary --- CHANGELOG.md | 1 + crates/event_core/src/instance.rs | 16 +++++- crates/event_core/src/intelligence.rs | 18 +++++-- .../tests/intelligence_status_contract.rs | 7 ++- .../tests/mention_instance_contract.rs | 54 +++++++++++++++++-- .../event-intelligence-status-gates.md | 4 +- 6 files changed, 88 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d4333ae..5a2e6cc5 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 +- `event_core` now requires and retains `EventEvidenceLayer::PromotedTransition` when constructing an `EventInstance`; every other layer is rejected at the promotion boundary, and TDT story classification uses a caller-owned hash set for expected constant-time membership checks. - `event_core` ADR 0016 evidence-status gates: TDT detections and CHRONOS predictions cannot admit a forward state transition; first-story detection scores miss/false-alarm rates against a known story stream (Allan 2002 task). - `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/crates/event_core/src/instance.rs b/crates/event_core/src/instance.rs index 98121632..4952cd74 100644 --- a/crates/event_core/src/instance.rs +++ b/crates/event_core/src/instance.rs @@ -1,6 +1,9 @@ //! Versioned event instances distinct from mentions. -use crate::{EventConfidence, EventError, EventInstanceId, EventMentionId, EventRoleKind}; +use crate::{ + EventConfidence, EventError, EventEvidenceLayer, EventInstanceId, EventMentionId, + EventRoleKind, admit_state_transition, +}; use temporal_core::{EventTime, TemporalBoundary, TemporalInterval, TemporalPrecision}; /// A versioned event instance with event-time support and typed roles. @@ -14,6 +17,7 @@ pub struct EventInstance { supporting_mentions: Vec, event_time: TemporalInterval, confidence: EventConfidence, + evidence_layer: EventEvidenceLayer, roles: Vec<(EventRoleKind, String)>, } @@ -24,12 +28,15 @@ impl EventInstance { /// /// Returns confidence or validity errors when inputs fail validation. /// At least one supporting mention is required. + /// The evidence layer must be [`EventEvidenceLayer::PromotedTransition`]. pub fn promote_from_mentions( supporting_mentions: Vec, valid_from: EventTime, valid_to: EventTime, confidence: EventConfidence, + evidence_layer: EventEvidenceLayer, ) -> Result { + admit_state_transition(evidence_layer)?; if supporting_mentions.is_empty() { return Err(EventError::InvalidWirePayload); } @@ -44,6 +51,7 @@ impl EventInstance { supporting_mentions, event_time, confidence, + evidence_layer, roles: Vec::new(), }) } @@ -72,6 +80,12 @@ impl EventInstance { self.confidence } + /// Return the independently promoted evidence layer retained by the instance. + #[must_use] + pub const fn evidence_layer(&self) -> EventEvidenceLayer { + self.evidence_layer + } + /// Attach a typed role argument. pub fn assign_role(&mut self, role: EventRoleKind, argument: impl Into) { self.roles.push((role, argument.into())); diff --git a/crates/event_core/src/intelligence.rs b/crates/event_core/src/intelligence.rs index f9596698..f0c24fe2 100644 --- a/crates/event_core/src/intelligence.rs +++ b/crates/event_core/src/intelligence.rs @@ -1,6 +1,7 @@ //! Evidence-status gates for TDT detection and CHRONOS prediction. use crate::EventError; +use std::{collections::HashSet, hash::BuildHasher}; /// Epistemic layer of an event-intelligence output. /// @@ -68,7 +69,10 @@ pub enum TdtStoryDecision { /// Classify one candidate against previously seen story identities. #[must_use] -pub fn classify_tdt_story(seen_story_ids: &[u64], candidate_story_id: u64) -> TdtStoryDecision { +pub fn classify_tdt_story( + seen_story_ids: &HashSet, + candidate_story_id: u64, +) -> TdtStoryDecision { if seen_story_ids.contains(&candidate_story_id) { TdtStoryDecision::Track } else { @@ -176,14 +180,22 @@ pub fn first_story_detection_rates( #[cfg(test)] mod tests { + use std::collections::HashSet; + use super::{ EventEvidenceLayer, TdtStoryDecision, classify_tdt_story, first_story_detection_rates, }; #[test] fn zero_denominator_rates_are_zero_and_track_is_not_first() { - assert_eq!(classify_tdt_story(&[7], 7), TdtStoryDecision::Track); - assert_eq!(classify_tdt_story(&[], 8), TdtStoryDecision::FirstStory); + assert_eq!( + classify_tdt_story(&HashSet::from([7]), 7), + TdtStoryDecision::Track + ); + assert_eq!( + classify_tdt_story(&HashSet::new(), 8), + TdtStoryDecision::FirstStory + ); assert!(first_story_detection_rates(&[], &[]).is_err()); assert!(first_story_detection_rates(&[true], &[true, false]).is_err()); let no_first_story = std::hint::black_box( diff --git a/crates/event_core/tests/intelligence_status_contract.rs b/crates/event_core/tests/intelligence_status_contract.rs index eaa4fe92..258a2a26 100644 --- a/crates/event_core/tests/intelligence_status_contract.rs +++ b/crates/event_core/tests/intelligence_status_contract.rs @@ -4,6 +4,7 @@ use event_core::{ EventError, EventEvidenceLayer, TdtStoryDecision, admit_state_transition, classify_tdt_story, first_story_detection_rates, }; +use std::collections::HashSet; #[test] fn only_promoted_transitions_enter_the_state_graph() { @@ -42,16 +43,14 @@ fn only_promoted_transitions_enter_the_state_graph() { fn first_story_detector_recovers_known_stream_with_computed_rates() { // Appearance order of news stories: first occurrence is a first story. let stream = [10_u64, 20, 10, 30, 20]; - let mut seen = Vec::new(); + let mut seen = HashSet::new(); let mut predicted = Vec::new(); let mut truth = Vec::new(); for story in stream { let decision = classify_tdt_story(&seen, story); predicted.push(matches!(decision, TdtStoryDecision::FirstStory)); truth.push(!seen.contains(&story)); - if !seen.contains(&story) { - seen.push(story); - } + seen.insert(story); } let rates = first_story_detection_rates(&truth, &predicted).expect("rates"); assert_eq!(rates.hits(), 3); diff --git a/crates/event_core/tests/mention_instance_contract.rs b/crates/event_core/tests/mention_instance_contract.rs index c5e92396..0b7d43bb 100644 --- a/crates/event_core/tests/mention_instance_contract.rs +++ b/crates/event_core/tests/mention_instance_contract.rs @@ -1,8 +1,8 @@ //! Realistic contracts: mentions are not instances; promotion is explicit. use event_core::{ - EventConfidence, EventError, EventInstance, EventMention, EventRegistry, EventRoleKind, - refuse_mention_as_instance, + EventConfidence, EventError, EventEvidenceLayer, EventInstance, EventMention, EventRegistry, + EventRoleKind, refuse_mention_as_instance, }; use evidence_core::{DocumentRecord, EvidenceId, SourceArtifact}; use temporal_core::EventTime; @@ -50,6 +50,7 @@ fn registry_requires_supporting_mentions_before_instance_insert() { start, end, EventConfidence::certain().expect("certain"), + EventEvidenceLayer::PromotedTransition, ) .expect("instance"); assert_eq!( @@ -65,6 +66,7 @@ fn registry_requires_supporting_mentions_before_instance_insert() { start, end, EventConfidence::certain().expect("certain"), + EventEvidenceLayer::PromotedTransition, ) .expect("instance"); instance.assign_role(EventRoleKind::Product, "contract award"); @@ -96,7 +98,8 @@ fn empty_surface_and_empty_mention_sets_fail_closed() { Vec::new(), start, end, - EventConfidence::certain().expect("c") + EventConfidence::certain().expect("c"), + EventEvidenceLayer::PromotedTransition, ) .map(|_| ()), Err(EventError::InvalidWirePayload) @@ -124,6 +127,7 @@ fn accessors_and_duplicate_identity_paths_are_covered() { start, end, EventConfidence::new(0.9).expect("c"), + EventEvidenceLayer::PromotedTransition, ) .expect("instance"); assert!((instance.confidence().value() - 0.9).abs() < f64::EPSILON); @@ -146,3 +150,47 @@ fn accessors_and_duplicate_identity_paths_are_covered() { Err(EventError::DuplicateEventIdentity) ); } + +#[test] +fn promotion_rejects_every_non_promoted_evidence_layer() { + let evidence_id = document_evidence(); + let mention = EventMention::new( + evidence_id, + "contract award announced", + EventConfidence::certain().expect("confidence"), + ) + .expect("mention"); + let start = EventTime::parse_rfc3339("2026-03-01T00:00:00Z").expect("start"); + let end = EventTime::parse_rfc3339("2026-03-01T23:59:59Z").expect("end"); + + for (layer, expected) in [ + ( + EventEvidenceLayer::ObservedMention, + EventError::DetectionIsNotTransition, + ), + ( + EventEvidenceLayer::TdtDetection, + EventError::DetectionIsNotTransition, + ), + ( + EventEvidenceLayer::ChronosPrediction, + EventError::PredictionIsNotFact, + ), + ( + EventEvidenceLayer::TemporalConsistency, + EventError::DetectionIsNotTransition, + ), + ] { + assert_eq!( + EventInstance::promote_from_mentions( + vec![mention.mention_id()], + start, + end, + EventConfidence::certain().expect("confidence"), + layer, + ) + .map(|_| ()), + Err(expected) + ); + } +} diff --git a/docs/research/event-intelligence-status-gates.md b/docs/research/event-intelligence-status-gates.md index 207af365..dc4a24f6 100644 --- a/docs/research/event-intelligence-status-gates.md +++ b/docs/research/event-intelligence-status-gates.md @@ -7,7 +7,8 @@ This note doctors the first ADR 0016 production slice in `event_core`: 1. every event-intelligence output carries an epistemic layer (`observed_mention`, `tdt_detection`, `chronos_prediction`, `temporal_consistency`, `promoted_transition`); 2. only an independently promoted transition may enter the forward state graph; 3. CHRONOS predictions are never treated as observed fact; -4. a first-story detector is scored with miss and false-alarm rates against a known story stream. +4. the concrete instance-promotion API retains the accepted layer and rejects every non-promoted layer; +5. a first-story detector is scored with miss and false-alarm rates against a known story stream. Full TDT tracking/calibration and CHRONOS schema extraction remain accepted-target. No database migration is allocated. @@ -24,6 +25,7 @@ Allan (2002) defines first-story detection as a scored measurement task with mis ## Verification - `admit_state_transition(PromotedTransition)` succeeds; +- `EventInstance::promote_from_mentions` retains `PromotedTransition` and rejects observed mentions, TDT detections, CHRONOS predictions, and temporal-consistency judgments; - TDT/mention/consistency layers return `DetectionIsNotTransition`; - CHRONOS predictions return `PredictionIsNotFact`; - stream `[10,20,10,30,20]` recovers three first stories with miss rate 0 and false-alarm rate 0; From 8f26c2f82bd6fc579baa3e72046f70d1b4973269 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 22 Aug 2026 05:13:47 +0900 Subject: [PATCH 18/19] test: cover event evidence layer accessor --- crates/event_core/tests/mention_instance_contract.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/event_core/tests/mention_instance_contract.rs b/crates/event_core/tests/mention_instance_contract.rs index 0b7d43bb..6aa48fe3 100644 --- a/crates/event_core/tests/mention_instance_contract.rs +++ b/crates/event_core/tests/mention_instance_contract.rs @@ -80,6 +80,10 @@ fn registry_requires_supporting_mentions_before_instance_insert() { assert!(registry.mention(mention.mention_id()).is_some()); let stored = registry.instance(instance.instance_id()).expect("stored"); assert_eq!(stored.supporting_mentions(), &[mention.mention_id()]); + assert_eq!( + stored.evidence_layer(), + EventEvidenceLayer::PromotedTransition + ); assert_eq!(stored.roles().len(), 2); assert!(stored.is_active_at(start)); } From d4cd08341e8dca8475542cf9f3b46a1f5bae5ea3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 23 Aug 2026 21:56:12 +0900 Subject: [PATCH 19/19] docs(adr): record provider-payload minimization as implemented-main ADR 0009 still described purpose-bound provider payloads as an active PR after that slice landed on protected main. Align the maturity line with TRACEABILITY and the validation ledger. --- docs/adr/0009-purpose-bound-pii-governance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/adr/0009-purpose-bound-pii-governance.md b/docs/adr/0009-purpose-bound-pii-governance.md index 88ed1341..e06de6e1 100644 --- a/docs/adr/0009-purpose-bound-pii-governance.md +++ b/docs/adr/0009-purpose-bound-pii-governance.md @@ -1,7 +1,7 @@ # ADR 0009 — Purpose-bound PII governance without blanket masking **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 +**Implementation maturity:** partial — persistence retention/deletion/legal-hold (migration `0007`) and purpose-bound provider-payload minimization (expired-purpose denial, log/source separation, separately authorized re-identification) are implemented-main; deployment/provider-region evidence remains accepted-target **Date:** 2026-08-10 **Supersedes:** None.