Typed materialization can introduce two current values for a functional state attribute without reconciling their timeline. For the #875 observation replay, a cup observed on the desk and later on the shelf retained both open typed rows; running rule derivation still left the desk-dependent step current.
This is a boundary/contract question as well as an implementation gap: should materialization reconcile changed functional predicates, or should callers be required to invoke reconciliation explicitly? A successful projection currently does not establish that its functional timeline is settled.
Reproduction
Declare location as a functional state attribute. Create two separately dated documents (08:00 desk, 08:10 shelf), insert their open statements, explicitly bind the phrase to location, then call typed materialization. These are store-level operations with no model and no HTTP adapter. The earlier desk row remains open. Calling temporal::reconcile_predicate explicitly closes the earlier interval; a subsequent derivation run retracts the current step while retaining its historical interval.
Expected boundary to settle: either reconcile affected functional timelines as part of the projection path, with tests for conflicts and history, or make the required caller-side reconciliation explicit. This report does not propose treating every stale statement as false or automatically resolving ambiguous observations.
Executable evidence
The complete fixture and reproduction are retained at the immutable experimental commit 4864e2f19e3ab0cee58d5ef4eade6212b3ae1af6 on the fork. Its base includes #884 and dev@9f50e3c. The test was explicitly executed with a real PostgreSQL 16/pgvector database and fails at the assertion shown below (exit 101), not during compilation or setup. It is kept outside #898's upstream test diff; it is a proposed behavior reproduction, not an ignored upstream acceptance test.
With that checkout and a migrated, dedicated test database:
UTOPIA_TEST_REQUIRE_DB=1 cargo test --locked -p utopia-store --test store \
a_plan_step_follows_its_premise::a_later_bound_statement_closes_the_earlier_place \
-- --ignored --exact --test-threads=1
UTOPIA_DATABASE_URL must point to that database. The helper definitions are in the linked file. The reproducing test is:
Store-level reproduction
#[tokio::test]
#[ignore = "#875 gap: materialize writes typed rows without timeline reconciliation"]
async fn a_later_bound_statement_closes_the_earlier_place() -> anyhow::Result<()> {
let Some(url) = utopia_store::test_db::url() else {
return Ok(());
};
let pool = PgPool::connect(&url).await?;
let f = seed(&pool, "issue875-materialize-timeline").await?;
let run = async {
let (_, c1) = document(&pool, &f, "obs-1.json", T1, "cup-7 is on desk").await?;
let (_, c2) = document(&pool, &f, "obs-2.json", T2, "cup-7 is on shelf").await?;
statement(&pool, &f, c1, "desk", T1).await?;
statement(&pool, &f, c2, "shelf", T2).await?;
bind_is_on(&pool, &f).await?;
materialize::materialize(&pool, f.kb).await?;
let desk = typed_desk(&pool, &f).await?;
assert_eq!(desk.len(), 1);
assert!(
desk[0].valid_to.is_some() || desk[0].valid_to_precision.is_some(),
"location is functional and a later place was materialized, \
yet the desk row is still open: {desk:?}"
);
anyhow::Ok(())
}
.await;
cleanup(&pool, f.org).await?;
run
}
Refs #875 and #898. No production fix is included in this report.
Typed materialization can introduce two current values for a functional state attribute without reconciling their timeline. For the #875 observation replay, a cup observed on the desk and later on the shelf retained both open typed rows; running rule derivation still left the desk-dependent step current.
This is a boundary/contract question as well as an implementation gap: should materialization reconcile changed functional predicates, or should callers be required to invoke reconciliation explicitly? A successful projection currently does not establish that its functional timeline is settled.
Reproduction
Declare
locationas a functional state attribute. Create two separately dated documents (08:00 desk, 08:10 shelf), insert their open statements, explicitly bind the phrase tolocation, then call typed materialization. These are store-level operations with no model and no HTTP adapter. The earlier desk row remains open. Callingtemporal::reconcile_predicateexplicitly closes the earlier interval; a subsequent derivation run retracts the current step while retaining its historical interval.Expected boundary to settle: either reconcile affected functional timelines as part of the projection path, with tests for conflicts and history, or make the required caller-side reconciliation explicit. This report does not propose treating every stale statement as false or automatically resolving ambiguous observations.
Executable evidence
The complete fixture and reproduction are retained at the immutable experimental commit
4864e2f19e3ab0cee58d5ef4eade6212b3ae1af6on the fork. Its base includes #884 anddev@9f50e3c. The test was explicitly executed with a real PostgreSQL 16/pgvector database and fails at the assertion shown below (exit 101), not during compilation or setup. It is kept outside #898's upstream test diff; it is a proposed behavior reproduction, not an ignored upstream acceptance test.With that checkout and a migrated, dedicated test database:
UTOPIA_TEST_REQUIRE_DB=1 cargo test --locked -p utopia-store --test store \ a_plan_step_follows_its_premise::a_later_bound_statement_closes_the_earlier_place \ -- --ignored --exact --test-threads=1UTOPIA_DATABASE_URLmust point to that database. The helper definitions are in the linked file. The reproducing test is:Store-level reproduction
Refs #875 and #898. No production fix is included in this report.