fix: reconcile erased Archive removals and exercise live compaction recording

Reconcile immutable Runtime removal input under a confirmed permanent erase fence. A confirmed source_removed receipt is audited without republishing erased archives; unknown receipt plus attested remaining aggregate permits an audited stale plan and proof-bound purge replacement. Unknown receipt and inventory remain unresolved. Distinguish domain tombstones from scoped Runtime physical erasure acknowledgements.

Erase diagnostic originals and interrupted staging bound to the same archived Session/removal operation, validating Workspace/Worker/fingerprint and saved receipts without rewriting their historical retained flags. Finalize Backend removal only after physical acknowledgement, including previous Retain disposition.

Connect public standalone protocol, real Worker request-threshold compaction twice within one turn, cancellation and real pre-AI input rollback, original canonical readers, existing Jobs and sequential Journal tools. Gate generation while compaction completes; verify all durable originals were served, summary/rolled-back input exclusion, exact final cursor, no extra input for successor drain, and one ongoing open entry. External model streams/usage are scripted, not semantic evaluation.

Validation: Backend1112lib+11bin (existing ignore1), Runtime429lib+38bin, standalone72lib+27integration (existing model ignore1), runtime-api10, root check/doc/fmt/diff/naming, Server/Runtime builds, isolated worker_environment2 passed. Earlier broad suites remain recorded on the prior source. Fixtures and injected boundaries are not arbitrary crash/remote-transport-loss coverage; worker_environment only verifies startup/connection/cleanup.

Acceptance clarification: T-734 assignment answer event22 explicitly defers real-model semantic condition6 to user use, excluding it from this completion scope. It remains unexecuted and unevaluated, not passed. Technical review findings still require a fresh independent verdict on this source.
This commit is contained in:
2026-10-16 04:58:53 +09:00
parent 5bd49760e5
commit b5a307811b
10 changed files with 1536 additions and 13 deletions
@@ -659,8 +659,38 @@ fn catalog_rejects_oversize_record_with_explicit_resource_limit() {
fn archive_scope_and_legacy_active_pointer_are_checked_and_erase_retry_never_restores_originals() {
let f = Fixture::new();
f.stop();
let request = f.retention_request(SessionDisposition::Archive);
f.runtime.execute_worker_retention(&request).unwrap();
let mut request = f.retention_request(SessionDisposition::Archive);
request.diagnostics_disposition = DiagnosticsDisposition::Retain;
let log = f
.root()
.parent()
.unwrap()
.join("runs/run-original/worker.out.log");
std::fs::create_dir_all(log.parent().unwrap()).unwrap();
std::fs::write(&log, "archived diagnostic original").unwrap();
let original_receipt = f.runtime.execute_worker_retention(&request).unwrap();
assert!(original_receipt.diagnostics_retained);
let diagnostics = f
.temp
.path()
.join("archives/diagnostics")
.join(&request.operation_id);
assert!(
diagnostics
.join("runs/run-original/worker.out.log")
.is_file()
);
let staged_diagnostics =
diagnostics.with_file_name(format!(".{}.tmp-42-1", request.operation_id));
std::fs::create_dir_all(&staged_diagnostics).unwrap();
std::fs::write(
staged_diagnostics.join("partial-original.log"),
"incomplete copy",
)
.unwrap();
let unrelated = diagnostics.with_file_name("another-operation");
std::fs::create_dir_all(&unrelated).unwrap();
std::fs::write(unrelated.join("untouched.log"), "unrelated diagnostic").unwrap();
let source = Source::Archived {
archive_id: "canonical-archive".into(),
};
@@ -756,12 +786,76 @@ fn archive_scope_and_legacy_active_pointer_are_checked_and_erase_retry_never_res
.pin_session_public_source_scoped(&f.scope, &source, &f.session.to_string(), pin)
.is_err()
);
// An old removal receipt is evidence only and must not republish erased bytes.
f.runtime.execute_worker_retention(&request).unwrap();
// An old receipt is historical evidence, not permission to republish erased bytes.
assert_eq!(
f.runtime.execute_worker_retention(&request).unwrap(),
original_receipt
);
assert!(!diagnostics.exists());
assert!(!staged_diagnostics.exists());
assert!(unrelated.join("untouched.log").is_file());
assert!(!root.exists());
assert!(retention.pins(f.session).unwrap().is_empty());
}
#[test]
fn erasure_rejects_mismatched_archived_diagnostics_then_resumes_under_the_same_fence() {
let f = Fixture::new();
f.stop();
let mut request = f.retention_request(SessionDisposition::Archive);
request.diagnostics_disposition = DiagnosticsDisposition::Retain;
let receipt = f.runtime.execute_worker_retention(&request).unwrap();
let source = Source::Archived {
archive_id: request.archive_id.clone().unwrap(),
};
let manifest = f
.temp
.path()
.join("archives/diagnostics")
.join(&request.operation_id)
.join("manifest.json");
let original: serde_json::Value =
serde_json::from_slice(&std::fs::read(&manifest).unwrap()).unwrap();
for field in ["workspace_id", "input_fingerprint", "source_worker_id"] {
let mut changed = original.clone();
changed[field] = serde_json::json!(uuid::Uuid::now_v7().to_string());
std::fs::write(&manifest, serde_json::to_vec(&changed).unwrap()).unwrap();
assert!(
f.runtime
.delete_session_public_source_scoped(
&f.scope,
&source,
&f.session.to_string(),
SessionDeletionIntent::ErasePermanently
)
.is_err()
);
assert!(manifest.exists());
assert!(matches!(
runtime_session_retention(f.temp.path()).ensure_live(f.session),
Err(session_store::StoreError::SessionErased(_))
));
}
std::fs::write(&manifest, serde_json::to_vec(&original).unwrap()).unwrap();
f.runtime
.delete_session_public_source_scoped(
&f.scope,
&source,
&f.session.to_string(),
SessionDeletionIntent::ErasePermanently,
)
.unwrap();
assert!(!manifest.parent().unwrap().exists());
assert_eq!(
f.runtime.execute_worker_retention(&request).unwrap(),
receipt
);
assert!(
receipt.diagnostics_retained,
"historical receipt must not be rewritten after erasure"
);
}
#[test]
fn material_request_bounds_return_explicit_unavailable_without_previews() {
let f = Fixture::new();
+88 -4
View File
@@ -297,14 +297,15 @@ pub(crate) fn remove_archived_session_copies(
}
let session_root = entry.path().join("session");
let owner = match fs::read(entry.path().join("manifest.json")) {
Ok(bytes) => Some(
serde_json::from_slice::<WorkerSessionArchiveManifest>(&bytes)?.source_session_id,
),
Ok(bytes) => Some(serde_json::from_slice::<WorkerSessionArchiveManifest>(
&bytes,
)?),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
Err(error) => return Err(error.into()),
};
if let Some(owner) = owner {
if owner == session.to_string() {
if owner.source_session_id == session.to_string() {
erase_archived_diagnostics(runtime_root, &owner)?;
match fs::remove_dir_all(&session_root) {
Ok(()) => (),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => (),
@@ -326,6 +327,89 @@ pub(crate) fn remove_archived_session_copies(
Ok(())
}
/// Erasure includes diagnostic originals of the same archived removal operation.
/// The immutable removal receipt is historical evidence and is never rewritten.
/// Staging names are bound to that exact operation; unrelated archives are untouched.
fn erase_archived_diagnostics(
runtime_root: &Path,
archive: &WorkerSessionArchiveManifest,
) -> Result<(), session_store::StoreError> {
let invalid = || {
session_store::StoreError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Archived diagnostics erasure identity mismatch",
))
};
if archive.schema_version != ARCHIVE_SCHEMA_VERSION {
return Err(invalid());
}
validate_id("operation_id", &archive.operation_id).map_err(|_| invalid())?;
let receipt_path = runtime_root
.join("retention/operations")
.join(format!("{}.json", archive.operation_id));
if let Some(receipt) = read_operation_receipt(&receipt_path).map_err(|_| invalid())? {
if receipt.request.workspace_id != archive.workspace_id
|| receipt.request.source_runtime_id != archive.source_runtime_id
|| receipt.request.worker_id != archive.source_worker_id
|| receipt.request.operation_id != archive.operation_id
|| receipt.request.input_fingerprint != archive.input_fingerprint
|| receipt.request.archive_id.as_ref() != Some(&archive.archive_id)
|| receipt.result.archive.as_ref() != Some(archive)
{
return Err(invalid());
}
}
let parent = runtime_root.join("archives/diagnostics");
let entries = match fs::read_dir(&parent) {
Ok(entries) => entries,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error.into()),
};
let staging_prefix = format!(".{}.tmp-", archive.operation_id);
for entry in entries {
let entry = entry?;
let name = entry.file_name();
let Some(name) = name.to_str() else {
continue;
};
let staged = name.strip_prefix(&staging_prefix).is_some_and(|suffix| {
suffix.split_once('-').is_some_and(|(pid, sequence)| {
!pid.is_empty()
&& !sequence.is_empty()
&& pid.bytes().all(|b| b.is_ascii_digit())
&& sequence.bytes().all(|b| b.is_ascii_digit())
})
});
if name != archive.operation_id && !staged {
continue;
}
if !entry.file_type()?.is_dir() {
return Err(invalid());
}
let manifest = match fs::read(entry.path().join("manifest.json")) {
Ok(bytes) => Some(serde_json::from_slice::<DiagnosticsArchiveManifest>(
&bytes,
)?),
Err(error) if error.kind() == std::io::ErrorKind::NotFound && staged => None,
Err(error) => return Err(error.into()),
};
if let Some(manifest) = manifest {
if manifest.schema_version != ARCHIVE_SCHEMA_VERSION
|| manifest.workspace_id != archive.workspace_id
|| manifest.source_runtime_id != archive.source_runtime_id
|| manifest.source_worker_id != archive.source_worker_id
|| manifest.operation_id != archive.operation_id
|| manifest.input_fingerprint != archive.input_fingerprint
{
return Err(invalid());
}
}
fs::remove_dir_all(entry.path())?;
}
sync_directory(&parent, "erase archived Worker diagnostics").map_err(|_| invalid())?;
Ok(())
}
pub(crate) fn retention_error(error: session_store::StoreError) -> RuntimeError {
RuntimeError::InvalidRequest(format!("Session retention rejected operation: {error}"))
}