Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@
`wsc_retained_evidence_export_modes` witness, keeping retained-evidence WSC
export coverage in the release gate without using Cargo's slow package-level
name filter.
- `cargo xtask test-slice durability-release` now includes the exact
`retained_reading_missing_payload_is_not_empty_success` witness, locking the
app-safe missing-retention posture for reading payloads, reading envelopes,
and retained receipt support.
- `warp-core` trusted runtime hosts now configure runtime WAL through
`TrustedRuntimeWalConfig`, including in-memory and filesystem-backed
adapters. `TrustedRuntimeWalStoreKind` exposes the configured adapter kind as
Expand Down
183 changes: 180 additions & 3 deletions crates/warp-core/tests/retained_evidence_ref_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ use warp_core::causal_wal::{
RecoveredRetentionIndexError, RetainedMaterialKind, RetainedMaterialRecord,
};
use warp_core::{
ContractEvidenceIdentity, ContractObstructionKind, ContractObstructionSubject,
ContractOperationKind, InstalledContractPackageId, RetainedEvidenceCoordinate,
RetainedEvidencePosture, RetainedEvidenceRef, RetainedEvidenceRole,
make_head_id, ContractEvidenceIdentity, ContractObstructionKind, ContractObstructionSubject,
ContractOperationKind, GlobalTick, InstalledContractPackageId, IntentOutcome,
IntentOutcomeDecision, IntentOutcomeObservation, ReceiptCorrelationRecord,
RetainedEvidenceCoordinate, RetainedEvidencePosture, RetainedEvidenceRef, RetainedEvidenceRole,
TickReceiptRejection, WorldlineId, WorldlineTick, WriterHeadKey,
};

fn hash(seed: u8) -> [u8; 32] {
Expand Down Expand Up @@ -58,6 +60,41 @@ fn semantic_blob_coordinate(role: RetainedBlobRole, semantic_seed: u8) -> Semant
}
}

fn writer_head(seed: u8) -> WriterHeadKey {
WriterHeadKey {
worldline_id: WorldlineId::from_bytes(hash(seed)),
head_id: make_head_id(&format!("retained-evidence-head-{seed}")),
}
}

fn receipt_correlation(contract: ContractEvidenceIdentity) -> ReceiptCorrelationRecord {
ReceiptCorrelationRecord {
ticketed_ingress_id: hash(21),
submission_id: hash(22),
ticket_digest: hash(23),
ingress_id: hash(24),
head_key: writer_head(25),
contract: Some(contract),
commit_global_tick: GlobalTick::from_raw(26),
worldline_tick_after: WorldlineTick::from_raw(27),
tick_receipt_digest: hash(28),
commit_hash: hash(29),
}
}

fn assert_missing_retention(posture: &RetainedEvidencePosture) {
assert_eq!(
posture.obstruction().map(|obstruction| obstruction.kind),
Some(ContractObstructionKind::MissingRetention)
);
assert!(matches!(
posture
.obstruction()
.map(|obstruction| &obstruction.subject),
Some(ContractObstructionSubject::Retention { .. })
));
}

#[test]
fn retained_evidence_ref_id_binds_semantic_coordinate() {
let content_hash = content_hash(b"same bytes");
Expand Down Expand Up @@ -158,6 +195,146 @@ fn missing_content_returns_missing_retention_obstruction_with_ref_id() {
);
}

#[test]
fn retained_reading_missing_payload_is_not_empty_success() {
let query_contract = contract(14, 15, ContractOperationKind::Query);
let reading_id = hash(16);
let payload_bytes = b"retained query payload bytes";
let envelope_coordinate = RetainedEvidenceCoordinate::new(
query_contract.clone(),
RetainedEvidenceRole::ReadingEnvelope,
reading_id,
);
let payload_ref = RetainedEvidenceRef::new(
RetainedEvidenceCoordinate::new(
query_contract,
RetainedEvidenceRole::ReadingPayload,
reading_id,
),
content_hash(payload_bytes),
payload_bytes.len() as u64,
);
let reading_postures = [
RetainedEvidencePosture::missing_coordinate(&envelope_coordinate),
RetainedEvidencePosture::missing_content(&payload_ref),
];

assert_eq!(reading_postures.len(), 2);
assert!(matches!(
&reading_postures[0],
RetainedEvidencePosture::MissingCoordinate { .. }
));
if let RetainedEvidencePosture::MissingCoordinate {
coordinate,
obstruction,
} = &reading_postures[0]
{
assert_eq!(coordinate.role, RetainedEvidenceRole::ReadingEnvelope);
assert_eq!(coordinate.semantic_digest, reading_id);
assert_eq!(obstruction.kind, ContractObstructionKind::MissingRetention);
assert_eq!(
obstruction.subject,
ContractObstructionSubject::Retention {
retention_id: coordinate.coordinate_id()
}
);
}
assert!(matches!(
&reading_postures[1],
RetainedEvidencePosture::MissingContent { .. }
));
if let RetainedEvidencePosture::MissingContent {
reference,
obstruction,
} = &reading_postures[1]
{
assert_eq!(
reference.coordinate.role,
RetainedEvidenceRole::ReadingPayload
);
assert_eq!(reference.coordinate.semantic_digest, reading_id);
assert_eq!(reference.content_hash, content_hash(payload_bytes));
assert_eq!(reference.byte_len, payload_bytes.len() as u64);
assert_eq!(obstruction.kind, ContractObstructionKind::MissingRetention);
assert_eq!(
obstruction.subject,
ContractObstructionSubject::Retention {
retention_id: reference.evidence_ref_id()
}
);
}
for posture in &reading_postures {
assert_missing_retention(posture);
}

let receipt_contract = contract(17, 18, ContractOperationKind::Mutation);
let correlation = receipt_correlation(receipt_contract.clone());
let applied = IntentOutcome::from_observation(IntentOutcomeObservation::Decided {
correlation: Box::new(correlation.clone()),
decision: IntentOutcomeDecision::Applied {
receipt_entry_index: 3,
rule_id: hash(30),
},
});
let rejected = IntentOutcome::from_observation(IntentOutcomeObservation::Decided {
correlation: Box::new(correlation.clone()),
decision: IntentOutcomeDecision::Rejected {
receipt_entry_index: 4,
rule_id: hash(31),
reason: TickReceiptRejection::FootprintConflict,
blocked_by: vec![3],
},
});

for outcome in [&applied, &rejected] {
assert!(matches!(
outcome,
IntentOutcome::Applied { .. } | IntentOutcome::Rejected { .. }
));
if let IntentOutcome::Applied { receipt, .. } | IntentOutcome::Rejected { receipt, .. } =
outcome
{
assert_eq!(receipt.contract, Some(receipt_contract.clone()));
assert!(matches!(
receipt.retained_evidence.as_slice(),
[RetainedEvidencePosture::MissingCoordinate { .. }]
));
if let [RetainedEvidencePosture::MissingCoordinate {
coordinate,
obstruction,
}] = receipt.retained_evidence.as_slice()
{
assert_eq!(coordinate.contract, receipt_contract);
assert_eq!(coordinate.role, RetainedEvidenceRole::ContractReceipt);
assert_eq!(coordinate.semantic_digest, correlation.tick_receipt_digest);
assert_eq!(obstruction.kind, ContractObstructionKind::MissingRetention);
}
}
}
assert!(matches!(
rejected,
IntentOutcome::Rejected {
reason: TickReceiptRejection::FootprintConflict,
ref blocked_by,
..
} if blocked_by == &[3]
));

let no_matching_receipt = IntentOutcome::from_observation(IntentOutcomeObservation::Decided {
correlation: Box::new(correlation),
decision: IntentOutcomeDecision::NoMatchingReceiptEntry {
tick_receipt_digest: hash(28),
},
});
assert!(matches!(
no_matching_receipt,
IntentOutcome::Obstructed {
obstruction,
..
} if obstruction.kind == ContractObstructionKind::MissingRetention
));
}

#[test]
fn available_retained_evidence_is_not_an_obstruction() {
let reference = RetainedEvidenceRef::new(
Expand Down
2 changes: 1 addition & 1 deletion docs/workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ The repo also exposes maintenance commands via `cargo xtask …`:
- `cargo xtask test-slice contract-path-release` runs the v0.1 local contract-host release witness: installed contract pipeline replay, reference trusted host loop, and the serious external consumer fixture.
- `cargo xtask test-slice runtime-wal-ack` runs the fast runtime WAL-backed ACK witness: app-facing acceptance rollback, scheduler tick receipt invariant checks, scheduler tick commit-before-publish, recovered indexes, CLI submission posture JSON, stale-claim guard, and generated man-page check.
- `cargo xtask test-slice durable-runtime-wal` runs the release-grade filesystem runtime WAL durability witness: filesystem ACK recovery, filesystem failure atomicity, CLI submission posture JSON, stale-claim guard, and generated man-page check.
- `cargo xtask test-slice durability-release` runs the joined WAL/WSC release witness: filesystem runtime WAL durability, WSC retained evidence recovery, WSC topology recovery, topology WAL recovery, typed missing-material obstruction, stale-claim guards, doctrine checks, and generated man-page freshness. This is a release-gate slice, not the fastest local edit loop.
- `cargo xtask test-slice durability-release` runs the joined WAL/WSC release witness: filesystem runtime WAL durability, WSC retained evidence recovery, app-safe missing-retention posture, WSC topology recovery, topology WAL recovery, typed missing-material obstruction, stale-claim guards, doctrine checks, and generated man-page freshness. This is a release-gate slice, not the fastest local edit loop.
- `cargo xtask pr-preflight` runs the default changed-scope pre-PR gate against `origin/main`.
- `cargo xtask pr-preflight --full` runs the broader explicit full pre-PR gate.
- `cargo xtask dind` runs the DIND (Deterministic Ironclad Nightmare Drills) harness locally.
Expand Down
21 changes: 20 additions & 1 deletion xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,14 @@ fn build_test_slice_commands(slice: TestSlice) -> Vec<Command> {
"causal_wal_tests",
"wsc_retained_evidence_export_modes",
]),
cargo_command([
"test",
"-p",
"warp-core",
"--test",
"retained_evidence_ref_tests",
"retained_reading_missing_payload_is_not_empty_success",
]),
cargo_command([
"test",
"-p",
Expand Down Expand Up @@ -6711,7 +6719,7 @@ mod tests {
#[test]
fn test_slice_durability_release_stays_explicit() {
let commands = build_test_slice_commands(TestSlice::DurabilityRelease);
assert_eq!(commands.len(), 12);
assert_eq!(commands.len(), 13);

let expected = [
(
Expand Down Expand Up @@ -6806,6 +6814,17 @@ mod tests {
"wsc_retained_evidence_export_modes",
],
),
(
"cargo",
vec![
"test",
"-p",
"warp-core",
"--test",
"retained_evidence_ref_tests",
"retained_reading_missing_payload_is_not_empty_success",
],
),
(
"cargo",
vec![
Expand Down
Loading