Skip to content

fix(temporal): reject terminal results completed before knowledge cutoff #598

Closed as not planned

Description

@seonghobae

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:

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_cutoff fails closed for both succeeded and failed terminal results;
  • completed_at == knowledge_cutoff remains valid;
  • completed_at > knowledge_cutoff remains 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

  1. seonghobae commented on Sep 19, 2026

    @seonghobae
    ContributorAuthor

    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.rs stating that SystemTime is distinct from KnowledgeCutoff and that a pre-cutoff terminal publication may be legitimate for a historical snapshot. That is consistent with temporal_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 by AvailableTime <= KnowledgeCutoff; it does not require terminal SystemTime >= 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 planned under 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions