Repository navigation
fix(temporal): reject terminal results completed before knowledge cutoff #598
Description
Finding
AnalysisRunTerminalResult::validate currently validates knowledge_cutoff and completed_at independently, but it does not enforce their temporal order. knowledge_cutoff is the latest AvailableTime permitted in a historical analysis, while completed_at is a SystemTime. Both are typed absolute instants in temporal_core.
As a result, a succeeded or failed terminal result can currently validate with completed_at < knowledge_cutoff. That permits a run to certify completion before the end of the evidence-availability window it claims to have admitted. For a leakage-safe historical run this is temporally impossible: the executor could admit evidence whose AvailableTime <= knowledge_cutoff even when that evidence lies after the claimed completion instant.
Observed owner state before repair:
- protected
main@a243f18da4a4ca8a8d068c39922537f1f8ed6ad0; - surviving Validation / Analysis Run vehicle feat(analysis): bind template-copy identity refusals to an analysis-run profile #416 at
03f8de2ed0a0fb842d2022d411814e440df7cfb4; crates/tepp_api/src/analysis_result.rscallsrequire_rfc3339_knowledge_cutoffand parsescompleted_atasSystemTime, but performs noSystemTime >= KnowledgeCutoffcomparison;- CWC child feat(analysis): bind CWC within/between slopes to an analysis-run profile #372 at
456f0a30f4dcaae10bc01bf902e83f75154d3dfbalready treatsAvailableTime > knowledge_cutoffas inadmissible and therefore depends on the terminal boundary not certifying a pre-cutoff completion.
This is a shared Analysis Run / Temporal Semantics invariant, not a CWC-specific numerical rule. Reusable psychometric arithmetic is out of scope.
RED acceptance
Add a public tepp_api contract proving that for the same valid request/receipt:
completed_at < knowledge_cutofffails closed for both succeeded and failed terminal results;completed_at == knowledge_cutoffremains valid;completed_at > knowledge_cutoffremains valid;- malformed timestamps continue to fail closed;
- equivalent RFC 3339 representations are compared by instant, not text.
Causal repair
Parse both clocks through temporal_core and require SystemTime::instant() >= KnowledgeCutoff::instant() in the terminal-result validation boundary. Do not clamp timestamps, rewrite the cutoff, infer completion time from wall clock, or add profile-specific workarounds.
Documentation / architecture handoff
The implementation belongs to the surviving Analysis Run vehicle. ADR 0022 / current Analysis Run temporal semantics and canonical TRACEABILITY/product-gap currentization belong to the #435 documentation single-writer lane; do not duplicate the shared documentation surface in a profile child.
Promotion gate
Keep this finding open until one unchanged surviving exact head has the RED→repair lineage plus Rust format/clippy/test/rustdoc/coverage and required security/review evidence. Predecessor checks do not transfer. No force push, destructive rebase, skip/xfail, coverage-denominator change, self-approval, or timestamp tolerance weakening.
Activity
seonghobae commentedon Sep 19, 2026
Re-verification rejects this hypothesis as a valid repair finding.
The current public contract already contains an explicit test in
crates/tepp_api/tests/analysis_result_contract.rsstating thatSystemTimeis distinct fromKnowledgeCutoffand that a pre-cutoff terminal publication may be legitimate for a historical snapshot. That is consistent withtemporal_core's nominal clock model: the clocks share an absolute-instant representation but do not acquire an ordering invariant merely because both can be compared. ADR 0022 constrains evidence admission byAvailableTime <= KnowledgeCutoff; it does not require terminalSystemTime >= KnowledgeCutoff.Enforcing the proposed ordering would therefore invent a new cross-clock semantic constraint and break an intentional public test rather than repair leakage. The actual leakage invariant remains that evidence after the knowledge cutoff cannot enter the scientific population. No production/test delta is valid here.
Closing as
not plannedunder the no-valid-delta exception. If a future product requirement wants wall-clock completion to dominate the cutoff, that requires an explicit Temporal Semantics decision/ADR rather than a bug fix.