Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e399ccc
test(sessions): cover a retired record its source offers again
ScriptedAlchemy Oct 20, 2026
dd7235c
fix(sessions): revive retired source records offered again
ScriptedAlchemy Oct 20, 2026
262a3de
test(global-db): harden source revival invariants
ScriptedAlchemy Oct 21, 2026
c8ad765
test(codex): seed retired rollout upgrade state
ScriptedAlchemy Oct 21, 2026
83ee6e4
fix(sessions): settle revived output collisions
ScriptedAlchemy Oct 21, 2026
56b7837
test(global-db): decode source generation text
ScriptedAlchemy Oct 21, 2026
10b6c47
Merge remote-tracking branch 'origin/master' into HEAD
ScriptedAlchemy Oct 22, 2026
11951ce
Merge master into retired source revival fix
ScriptedAlchemy Oct 22, 2026
b679e44
test(sessions): reject revival after identity collision
ScriptedAlchemy Oct 22, 2026
d79de82
fix(observation): queue revival only after accepted source offers
ScriptedAlchemy Oct 22, 2026
19b2ca0
Merge branch 'master' into fix/4371-revive-retired-source-record
ScriptedAlchemy Oct 22, 2026
c6f0ab0
fix(observation): retain original authority on exact retries
ScriptedAlchemy Oct 22, 2026
f8f7724
fix(lsp): observe analyzer exit after transport EOF
ScriptedAlchemy Oct 22, 2026
4f5c153
docs(lsp): keep the exit observation invariant concise
ScriptedAlchemy Oct 22, 2026
ba3c7ab
test(mcp): await descendant readiness before testing its deadline
ScriptedAlchemy Oct 22, 2026
6f79b45
test(index): hold the live text slice before checking overflow
ScriptedAlchemy Oct 22, 2026
0a3d8e1
test(runtime): retry typed transcript readiness before restart
ScriptedAlchemy Oct 22, 2026
e842965
test(daemon): retry in-flight discovery during detached warmup
ScriptedAlchemy Oct 22, 2026
c839a87
test(fs): verify batch barriers without disk latency ratios
ScriptedAlchemy Oct 22, 2026
3391962
Merge master into source revival repairs
ScriptedAlchemy Oct 22, 2026
1634436
Merge remote-tracking branch 'origin/master' into fix/4371-revive-ret…
ScriptedAlchemy Oct 22, 2026
61332c1
Merge commit 'refs/review/sweep-current-master' into HEAD
ScriptedAlchemy Oct 22, 2026
099de47
build(bazel): regenerate private filesystem test targets
ScriptedAlchemy Oct 22, 2026
26b7fcb
Merge master and preserve both projection queue guards
ScriptedAlchemy Oct 22, 2026
38cc6b0
Merge origin/master into fix/4371-revive-retired-source-record
ScriptedAlchemy Oct 22, 2026
e2159b3
Merge branch 'master' into fix/4371-revive-retired-source-record
ScriptedAlchemy Oct 22, 2026
e1ad869
Merge branch 'master' into fix/4371-revive-retired-source-record
ScriptedAlchemy Oct 22, 2026
4e701e7
Merge branch 'master' into fix/4371-revive-retired-source-record
ScriptedAlchemy Oct 22, 2026
bce42ac
Merge master and preserve source revival durability tests
ScriptedAlchemy Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions crates/tracedecay-global-db/src/observation/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,12 @@ const OBSERVATION_AUTHORITY_SCHEMA_SQL: &str = "CREATE TABLE IF NOT EXISTS sanit
attempt_count INTEGER NOT NULL DEFAULT 0 CHECK(attempt_count >= 0),
next_retry_at_micros INTEGER NOT NULL DEFAULT 0 CHECK(next_retry_at_micros >= 0),
last_error TEXT,
revival_generation TEXT,
revival_start_offset INTEGER CHECK(
(revival_generation IS NULL AND revival_start_offset IS NULL)
OR (revival_generation IS NOT NULL AND revival_start_offset IS NOT NULL
AND revival_start_offset >= 0)
),
FOREIGN KEY(observation_id) REFERENCES observations(observation_id)
);
CREATE INDEX IF NOT EXISTS idx_retrieval_anchor_dispositions_release_due
Expand All @@ -263,6 +269,39 @@ pub async fn ensure_observation_schema(
conn.execute_batch(OBSERVATION_AUTHORITY_SCHEMA_SQL)
.await
.map_err(|error| global_db_operation_error(OBSERVATION_SCHEMA_OPERATION, error))?;
for (column, alteration) in [
(
"revival_generation",
"ALTER TABLE projection_queue ADD COLUMN revival_generation TEXT",
),
(
"revival_start_offset",
"ALTER TABLE projection_queue ADD COLUMN revival_start_offset INTEGER CHECK(
(revival_generation IS NULL AND revival_start_offset IS NULL)
OR (revival_generation IS NOT NULL AND revival_start_offset IS NOT NULL
AND revival_start_offset >= 0)
)",
),
] {
let mut rows = conn
.query(
"SELECT 1 FROM pragma_table_xinfo('projection_queue') WHERE name = ?1",
[column],
)
.await
.map_err(|error| global_db_operation_error(OBSERVATION_SCHEMA_OPERATION, error))?;
let present = rows
.next()
.await
.map_err(|error| global_db_operation_error(OBSERVATION_SCHEMA_OPERATION, error))?
.is_some();
drop(rows);
if !present {
conn.execute(alteration, ())
.await
.map_err(|error| global_db_operation_error(OBSERVATION_SCHEMA_OPERATION, error))?;
}
}
if !table_preexisted {
for migration in [
OBSERVATION_SCHEMA_MIGRATION,
Expand Down
212 changes: 55 additions & 157 deletions crates/tracedecay-global-db/src/observation_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,82 +459,7 @@ impl GlobalDbObservationStore {
));
}
if canonical_payload_revision {
let Some(existing) = existing.as_ref() else {
return Err(runtime_storage_error(
"persist canonical payload revision",
"classified revision replay has no retained observation",
));
};
let identity = observation.identity();
let actual_cursor = actual_cursor();
let revision_covered = actual_cursor.as_ref().is_some_and(|cursor| {
cursor.generation() == identity.generation()
&& cursor.ordering_domain() == identity.ordering_domain()
&& cursor.position() >= identity.position().end()
});
if revision_covered {
return Ok(PreparedObservationPersist::ready(
ObservationPersistOutcome::CoveredDuplicate(
ObservationCommitReceipt::new(
existing.sequence(),
existing.observation().clone(),
write.next_cursor().clone(),
existing.retrieval_anchor().clone(),
existing.projection_generation().clone(),
)?
.with_repository_provenance_attachment(
existing.repository_provenance_attachment().clone(),
)?,
),
existing,
));
}
if known_cursor.is_some() {
return Ok(PreparedObservationPersist::AwaitsDurablePredecessor(
Box::new(write),
));
}
let mut advance = ObservationCursorAdvance::for_ordering_with_sanitization_receipt(
identity.source().clone(),
identity.scope().clone(),
identity.generation(),
identity.ordering_domain(),
write.expected_cursor().cloned(),
identity.position(),
ObservationCoverageReason::CanonicalPayloadRevision,
observation.receipt().clone(),
)?;
match (
write.next_cursor().file_identity(),
write.next_cursor().resume_fingerprint(),
) {
(Some(file_identity), Some(resume_fingerprint)) => {
advance = advance.with_resume_checkpoint(file_identity, resume_fingerprint);
}
(None, None) => {}
_ => {
return Err(runtime_storage_error(
"persist canonical payload revision",
"cursor resume checkpoint is incomplete",
));
}
}
self.advance_source_cursor(advance).await?;
return Ok(PreparedObservationPersist::ready(
ObservationPersistOutcome::CoveredDuplicate(
ObservationCommitReceipt::new(
existing.sequence(),
existing.observation().clone(),
write.next_cursor().clone(),
existing.retrieval_anchor().clone(),
existing.projection_generation().clone(),
)?
.with_repository_provenance_attachment(
existing.repository_provenance_attachment().clone(),
)?,
),
existing,
));
return Ok(PreparedObservationPersist::Submit(Box::new(write)));
}
let same_identity = pending
.as_ref()
Expand Down Expand Up @@ -607,23 +532,33 @@ impl GlobalDbObservationStore {
retained.observation().receipt() == observation.receipt()
}));
if existed_exact {
if pending.is_some() {
return Ok(PreparedObservationPersist::DeferredExactDuplicate(
Box::new(write),
// An exact retry retains its first accepted derived authority;
// later projection clocks or Git captures cannot replace it. The
// writer still serializes source revival against retirement.
let (anchor, provenance) = if let Some(retained) = pending.as_ref() {
(&retained.retrieval_anchor, &retained.repository_provenance)
} else if let Some(retained) = existing {
(
retained.retrieval_anchor(),
retained.repository_provenance_attachment(),
)
} else {
return Err(runtime_storage_error(
"persist exact observation retry",
"exact duplicate has no retained observation authority",
));
}
let Some(existing) = existing.as_ref() else {
return Err(ObservationStoreError::Storage {
operation: "persist_observation",
source: Box::new(std::io::Error::other(
"exact duplicate classification requires a stored observation",
)),
});
};
return Ok(PreparedObservationPersist::ready(
ObservationPersistOutcome::ExactDuplicate(existing.commit_receipt().clone()),
existing,
));
let replay = AnchoredObservationWrite::new(
write.write().clone(),
anchor.clone(),
anchor.projection_generation().clone(),
)?
.with_repository_provenance_attachment(
provenance.availability().clone(),
provenance.anchor().cloned(),
)?
.with_identity_collision_disposition(write.identity_collision_disposition());
return Ok(PreparedObservationPersist::ExactDuplicate(Box::new(replay)));
}
if pending.is_none() && existing.is_none() {
batch_state.register_pending(&write)?;
Expand Down Expand Up @@ -667,7 +602,7 @@ impl GlobalDbObservationStore {
let mut outcomes: Vec<Option<ObservationBatchPersistOutcome>> =
Vec::with_capacity(writes.len());
let mut submits = Vec::new();
let mut deferred_exact_duplicates = Vec::new();
let mut exact_duplicates = HashSet::new();
let mut awaiting = Vec::new();
let mut writes = writes.into_iter();
while let Some(write) = writes.next() {
Expand All @@ -681,13 +616,13 @@ impl GlobalDbObservationStore {
.prepare_observation_persist(write, &preflight, &mut batch_state, known_cursor)
.await?
{
PreparedObservationPersist::Ready(outcome) => outcomes.push(Some(*outcome)),
PreparedObservationPersist::Submit(write) => {
submits.push((outcomes.len(), *write));
outcomes.push(None);
}
PreparedObservationPersist::DeferredExactDuplicate(write) => {
deferred_exact_duplicates.push((outcomes.len(), *write));
PreparedObservationPersist::ExactDuplicate(write) => {
exact_duplicates.insert(outcomes.len());
submits.push((outcomes.len(), *write));
outcomes.push(None);
}
PreparedObservationPersist::AwaitsDurablePredecessor(write) => {
Expand All @@ -705,21 +640,19 @@ impl GlobalDbObservationStore {
published_cursors.insert(key, next_cursor);
}
if !submits.is_empty() {
let submitted = submit_observation_writes(
&self.database,
&self.runtime,
submits,
deferred_exact_duplicates,
)
.await?;
let submitted =
submit_observation_writes(&self.database, &self.runtime, submits).await?;
for (slot, outcome) in submitted {
outcomes[slot] = Some(outcome);
outcomes[slot] = Some(if exact_duplicates.contains(&slot) {
let (outcome, stored) = outcome.into_parts();
ObservationBatchPersistOutcome::new(
ObservationPersistOutcome::ExactDuplicate(outcome.receipt().clone()),
stored,
)
} else {
outcome
});
}
} else if !deferred_exact_duplicates.is_empty() {
return Err(runtime_storage_error(
"persist_observations",
"deferred duplicate has no preceding batch submission",
));
}
let outcomes = outcomes
.into_iter()
Expand Down Expand Up @@ -857,6 +790,8 @@ struct PendingObservationAuthority {
payload_reference: PayloadReferenceV1,
identity: ObservationIdentityMaterialV1,
receipt: SanitizationReceiptV1,
retrieval_anchor: RetrievalAnchorRecord,
repository_provenance: RepositoryProvenanceAttachmentV1,
}

#[derive(Clone)]
Expand Down Expand Up @@ -923,6 +858,8 @@ impl ObservationBatchState {
payload_reference: write.observation().payload_reference().clone(),
identity: write.observation().identity().clone(),
receipt: write.observation().receipt().clone(),
retrieval_anchor: write.retrieval_anchor().clone(),
repository_provenance: write.repository_provenance_attachment().clone(),
},
);
self.pending_receipts.insert(
Expand Down Expand Up @@ -1522,28 +1459,13 @@ fn decode_json<T: serde::de::DeserializeOwned>(
}

enum PreparedObservationPersist {
// Both payloads are large and only one is ever live, so both are boxed:
// leaving either inline makes every value of this enum carry that variant's
// footprint.
Ready(Box<ObservationBatchPersistOutcome>),
Submit(Box<AnchoredObservationWrite>),
DeferredExactDuplicate(Box<AnchoredObservationWrite>),
/// The write collides with, or must compare-and-set a source cursor
/// published by, an earlier member of this batch that is not durable yet.
/// The batch commits its prefix first and re-prepares this write against
/// the durable result, so it settles exactly as it would alone.
ExactDuplicate(Box<AnchoredObservationWrite>),
/// The write collides with an earlier member of this batch that is not
/// durable yet. Commit the prefix before classifying the collision.
AwaitsDurablePredecessor(Box<AnchoredObservationWrite>),
}

impl PreparedObservationPersist {
fn ready(outcome: ObservationPersistOutcome, stored: &StoredObservation) -> Self {
Self::Ready(Box::new(ObservationBatchPersistOutcome::new(
outcome,
Some(stored.clone()),
)))
}
}

impl ObservationStore for GlobalDbObservationStore {
async fn persist_observation(
&self,
Expand Down Expand Up @@ -2041,19 +1963,10 @@ async fn submit_observation_writes(
database: &Database,
runtime: &DatabaseRuntimeClientV1,
writes: Vec<(usize, AnchoredObservationWrite)>,
deferred_exact_duplicates: Vec<(usize, AnchoredObservationWrite)>,
) -> ObservationStoreResult<Vec<(usize, ObservationBatchPersistOutcome)>> {
let batches = partition_observation_writes(writes)?;
let mut deferred_exact_duplicates = Some(deferred_exact_duplicates);
let mut outcomes = Vec::new();
let batch_count = batches.len();
for (index, batch) in batches.into_iter().enumerate() {
let deferred = if index + 1 == batch_count {
deferred_exact_duplicates.take().unwrap_or_default()
} else {
Vec::new()
};
outcomes.extend(submit_observation_write_batch(database, runtime, batch, deferred).await?);
for batch in partition_observation_writes(writes)? {
outcomes.extend(submit_observation_write_batch(database, runtime, batch).await?);
}
Ok(outcomes)
}
Expand Down Expand Up @@ -2115,7 +2028,6 @@ async fn submit_observation_write_batch(
database: &Database,
runtime: &DatabaseRuntimeClientV1,
writes: Vec<(usize, AnchoredObservationWrite)>,
deferred_exact_duplicates: Vec<(usize, AnchoredObservationWrite)>,
) -> ObservationStoreResult<Vec<(usize, ObservationBatchPersistOutcome)>> {
let admitted_at = now_micros();
let command = serde_json::json!({
Expand Down Expand Up @@ -2191,7 +2103,6 @@ async fn submit_observation_write_batch(
const READBACK_OPERATION: &str = "read committed observation batch";
let observation_ids = writes
.iter()
.chain(deferred_exact_duplicates.iter())
.map(|(_, write)| write.observation().observation_id().clone())
.collect::<Vec<_>>();
let snapshot = begin_observation_read_snapshot(database, READBACK_OPERATION).await?;
Expand All @@ -2202,7 +2113,7 @@ async fn submit_observation_write_batch(
.commit()
.await
.map_err(|error| runtime_storage_error(READBACK_OPERATION, error))?;
let mut outcomes = writes
let outcomes = writes
.into_iter()
.map(|(slot, write)| {
let retained = stored
Expand All @@ -2224,20 +2135,6 @@ async fn submit_observation_write_batch(
})
})
.collect::<ObservationStoreResult<Vec<_>>>()?;
for (slot, write) in deferred_exact_duplicates {
let retained = stored
.get(write.observation().observation_id().as_str())
.ok_or_else(|| {
runtime_storage_error("read committed observation", "row unavailable")
})?;
outcomes.push((
slot,
ObservationBatchPersistOutcome::new(
ObservationPersistOutcome::ExactDuplicate(retained.commit_receipt().clone()),
Some(retained.clone()),
),
));
}
Ok(outcomes)
}

Expand All @@ -2258,9 +2155,10 @@ fn persist_outcome_from_submit(
RuntimeSubmitOutcomeV1::Committed { .. }
| RuntimeSubmitOutcomeV1::CommittedAfterCancellation { .. }
| RuntimeSubmitOutcomeV1::ExactReplay { .. }
if stored.observation().identity() != candidate.identity()
&& classify_observation_collision(stored.observation(), candidate)
== ObservationCollisionOutcomeV1::ExactDuplicate =>
if is_canonical_payload_revision_replay(stored.observation(), candidate)
|| (stored.observation().identity() != candidate.identity()
&& classify_observation_collision(stored.observation(), candidate)
== ObservationCollisionOutcomeV1::ExactDuplicate) =>
{
Ok(ObservationPersistOutcome::CoveredDuplicate(
ObservationCommitReceipt::new(
Expand Down
Loading
Loading