Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a5a7f30
chore: record implementation plan
Copilot Oct 19, 2026
e19be43
simplify(storage-report): bound metadata sampling
Copilot Oct 19, 2026
c73e9ef
fix(storage-report): budget retention inventory scans
Copilot Oct 19, 2026
30f62b9
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 20, 2026
7b7d8ee
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 20, 2026
6c51acb
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 20, 2026
299a4b7
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 21, 2026
a8b0828
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 21, 2026
2d4b08f
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 21, 2026
f829e73
fix(storage): share report budgets and preserve coverage
ScriptedAlchemy Oct 21, 2026
f162dfb
fix(storage): finish migrating bounded census callers
ScriptedAlchemy Oct 21, 2026
91c0d36
fix(storage): clear bounded report compiler warnings
ScriptedAlchemy Oct 21, 2026
b0d45bb
test(storage): keep zero-byte budget fixture consistent
ScriptedAlchemy Oct 21, 2026
5193a8b
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 21, 2026
965408d
Merge branch 'master' into copilot/simplify-storage-report-sizing
ScriptedAlchemy Oct 21, 2026
fe58c29
test(index): settle graph admission before pinning save baseline
ScriptedAlchemy Oct 21, 2026
f6393bf
fix(storage): charge directory enumeration to sizing budget
ScriptedAlchemy Oct 22, 2026
5692cbf
Merge remote-tracking branch 'origin/master' into HEAD
ScriptedAlchemy Oct 22, 2026
f9c6974
fix(retention): refuse symlinked offline report roots
ScriptedAlchemy Oct 22, 2026
03212da
test(hosts): avoid Linux native cache copy offload
ScriptedAlchemy Oct 22, 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
3 changes: 2 additions & 1 deletion crates/tracedecay-agent-hosts/src/agents/claude/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ fn copy_rendered_bundle_to_native_cache(home: &Path, tracedecay_bin: &str) {
for (relative, _) in rendered_plugin_files(tracedecay_bin, &ServeArgs::default()).unwrap() {
let target = cache.join(relative);
std::fs::create_dir_all(target.parent().unwrap()).unwrap();
std::fs::copy(source.join(relative), target).unwrap();
let contents = std::fs::read(source.join(relative)).unwrap();
std::fs::write(target, contents).unwrap();
}
}

Expand Down
260 changes: 252 additions & 8 deletions crates/tracedecay-cli/src/commands/profile_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ use tracedecay_runtime_core::native_runtime;
use crate::cli::ProfileStorageAction;
use tracedecay_contracts::retrieval::{AdminCliResultV1, AdminCliSurfaceRequestV1};
use tracedecay_global_db::profile_registry_maintenance::remove_store_directory;
use tracedecay_maintenance::retention::storage_report::{
StorageReport, StorageReportCoverageState, StorageReportSizeCoverageV1,
StorageReportSizeReasonV1, StorageReportSizeStateV1, StoreSizeReportEntry,
};
use tracedecay_runtime_core::lifecycle_lease::{
ExclusiveLeaseAttempt, try_acquire_exclusive_for_profile,
};
Expand Down Expand Up @@ -310,10 +314,21 @@ async fn brokered_storage_report(
Ok(report)
}

fn merge_storage_report_page(
report: &mut tracedecay_maintenance::retention::storage_report::StorageReport,
page: tracedecay_maintenance::retention::storage_report::StorageReport,
) {
fn merge_storage_report_page(report: &mut StorageReport, page: StorageReport) {
// Project pages and empty directory pages carry no backlog sample. Their
// default Complete coverage is neutral, not evidence of measured bytes.
let unregistered_size = if !has_unregistered_size_sample(report) {
page.coverage.unregistered_size
} else if !has_unregistered_size_sample(&page) {
report.coverage.unregistered_size
} else {
report
.coverage
.unregistered_size
.merge(page.coverage.unregistered_size)
};
let unregistered_count_is_lower_bound = report.coverage.unregistered_count_is_lower_bound
|| page.coverage.unregistered_count_is_lower_bound;
if report.profile_root.is_empty() {
report.profile_root = page.profile_root;
}
Expand All @@ -335,6 +350,14 @@ fn merge_storage_report_page(
.user_sessions_db_bytes
.max(page.user_sessions_db_bytes);
report.coverage = page.coverage;
report.coverage.unregistered_size = unregistered_size;
report.coverage.unregistered_count_is_lower_bound = unregistered_count_is_lower_bound;
}

fn has_unregistered_size_sample(report: &StorageReport) -> bool {
report.unregistered_dir_count > 0
|| report.unregistered_bytes > 0
|| report.coverage.unregistered_size != StorageReportSizeCoverageV1::default()
}

/// Read-only per-store size / free-page-ratio / unregistered-directory report
Expand Down Expand Up @@ -455,11 +478,12 @@ async fn handle_storage_report(
let root_state = store
.root_state
.map_or("root not probed", |state| state.label());
let size_coverage = effective_store_size_coverage(store);
println!(
" {} ({}, {root_state}): {} total, {free}",
" {} ({}, {root_state}): {}, {free}",
store.project_id,
store.canonical_root,
format_bytes(store.total_bytes),
storage_size_label(store.total_bytes, size_coverage),
);
let kinds = &store.kinds;
for (family, bytes) in [
Expand All @@ -480,6 +504,17 @@ async fn handle_storage_report(
store.unavailable_entry_count
);
}
if size_coverage.state
!= tracedecay_maintenance::retention::storage_report::StorageReportSizeStateV1::Complete
{
println!(
" sizing: {} ({})",
storage_coverage_state_label(size_coverage.state),
size_coverage
.reason
.map_or("unspecified", storage_coverage_reason_label)
);
}
}
for retention in &report.code_generation_retention {
println!(
Expand Down Expand Up @@ -535,12 +570,36 @@ async fn handle_storage_report(
}
println!(
" unregistered directories: {} ({})",
report.unregistered_dir_count,
format_bytes(report.unregistered_bytes)
storage_directory_count_label(
report.unregistered_dir_count,
report.coverage.unregistered_count_is_lower_bound
|| report.coverage.state == StorageReportCoverageState::Partial,
),
storage_size_label(report.unregistered_bytes, report.coverage.unregistered_size)
);
if report.coverage.unregistered_size.state
!= tracedecay_maintenance::retention::storage_report::StorageReportSizeStateV1::Complete
{
println!(
" sizing: {} ({})",
storage_coverage_state_label(report.coverage.unregistered_size.state),
report
.coverage
.unregistered_size
.reason
.map_or("unspecified", storage_coverage_reason_label)
);
}
if report.coverage.unregistered_size.unavailable_entry_count > 0 {
println!(
" not sized: {} unreadable or non-regular entries",
report.coverage.unregistered_size.unavailable_entry_count
);
}
if report.unregistered_dir_count > 0 {
println!(" run the daemon's automatic sweep to reclaim unregistered directories");
}

if let Some(footprint) = &report.footprint {
for line in footprint_lines(footprint) {
println!("{line}");
Expand All @@ -561,6 +620,61 @@ async fn handle_storage_report(
Ok(())
}

fn storage_size_label(bytes: u64, coverage: StorageReportSizeCoverageV1) -> String {
match coverage.state {
StorageReportSizeStateV1::Complete => format!("{} total", format_bytes(bytes)),
StorageReportSizeStateV1::Partial => format!("at least {} (partial)", format_bytes(bytes)),
StorageReportSizeStateV1::Unavailable => {
format!("size unavailable ({} measured)", format_bytes(bytes))
}
}
}

fn storage_directory_count_label(count: usize, is_lower_bound: bool) -> String {
if is_lower_bound {
format!("at least {count}")
} else {
count.to_string()
}
}

fn effective_store_size_coverage(store: &StoreSizeReportEntry) -> StorageReportSizeCoverageV1 {
let mut coverage = store.size_coverage;
coverage.unavailable_entry_count = coverage
.unavailable_entry_count
.max(store.unavailable_entry_count);
if coverage.state == StorageReportSizeStateV1::Complete && store.unavailable_entry_count > 0 {
coverage.state = if store.total_bytes == 0 {
StorageReportSizeStateV1::Unavailable
} else {
StorageReportSizeStateV1::Partial
};
coverage.reason = Some(StorageReportSizeReasonV1::UnreadableEntries);
}
if coverage.state == StorageReportSizeStateV1::Complete && coverage.unavailable_entry_count == 0
{
return StorageReportSizeCoverageV1::default();
}
coverage
}

fn storage_coverage_state_label(state: StorageReportSizeStateV1) -> &'static str {
match state {
StorageReportSizeStateV1::Complete => "complete",
StorageReportSizeStateV1::Partial => "partial lower bound",
StorageReportSizeStateV1::Unavailable => "unavailable",
}
}

fn storage_coverage_reason_label(reason: StorageReportSizeReasonV1) -> &'static str {
match reason {
StorageReportSizeReasonV1::UnreadableEntries => "unreadable entries",
StorageReportSizeReasonV1::EntryBudgetExceeded => "entry budget exceeded",
StorageReportSizeReasonV1::Cancelled => "cancelled",
StorageReportSizeReasonV1::DeadlineExceeded => "deadline exceeded",
}
}

/// The footprint section: every entry against its bound, largest first, then
/// the profile total.
fn footprint_lines(
Expand Down Expand Up @@ -641,6 +755,136 @@ mod footprint_output_tests {
}
}

#[cfg(test)]
mod storage_coverage_output_tests {
use tracedecay_maintenance::retention::storage_report::{
StorageReport, StorageReportCoverage, StorageReportSizeCoverageV1,
StorageReportSizeReasonV1, StorageReportSizeStateV1,
};

use super::{merge_storage_report_page, storage_directory_count_label, storage_size_label};

#[test]
fn unavailable_backlog_stays_unavailable_across_empty_and_unavailable_pages() {
let mut report = StorageReport::default();
let empty_page = StorageReport {
profile_root: "/profile".to_owned(),
..StorageReport::default()
};
merge_storage_report_page(&mut report, empty_page.clone());
let unavailable_page = StorageReport {
profile_root: "/profile".to_owned(),
unregistered_dir_count: 1,
coverage: StorageReportCoverage {
unregistered_size: StorageReportSizeCoverageV1 {
state: StorageReportSizeStateV1::Unavailable,
reason: Some(StorageReportSizeReasonV1::UnreadableEntries),
unavailable_entry_count: 1,
},
..StorageReportCoverage::default()
},
..StorageReport::default()
};
merge_storage_report_page(&mut report, unavailable_page.clone());
assert_eq!(
report.coverage.unregistered_size.state,
StorageReportSizeStateV1::Unavailable
);
merge_storage_report_page(&mut report, unavailable_page);
merge_storage_report_page(&mut report, empty_page);
assert_eq!(report.unregistered_dir_count, 2);
assert_eq!(report.unregistered_bytes, 0);
assert_eq!(
report.coverage.unregistered_size.state,
StorageReportSizeStateV1::Unavailable
);
assert_eq!(report.coverage.unregistered_size.unavailable_entry_count, 2);

merge_storage_report_page(
&mut report,
StorageReport {
profile_root: "/profile".to_owned(),
unregistered_dir_count: 1,
unregistered_bytes: 7,
..StorageReport::default()
},
);
assert_eq!(report.unregistered_bytes, 7);
assert_eq!(
report.coverage.unregistered_size.state,
StorageReportSizeStateV1::Partial
);
}

#[test]
fn truncated_directory_counts_remain_lower_bounds_after_page_aggregation() {
let mut report = StorageReport::default();
merge_storage_report_page(
&mut report,
StorageReport {
coverage: StorageReportCoverage {
unregistered_count_is_lower_bound: true,
..StorageReportCoverage::default()
},
..StorageReport::default()
},
);
merge_storage_report_page(
&mut report,
StorageReport {
unregistered_dir_count: 2,
..StorageReport::default()
},
);
assert!(report.coverage.unregistered_count_is_lower_bound);
assert_eq!(storage_directory_count_label(0, true), "at least 0");
assert_eq!(
storage_directory_count_label(
report.unregistered_dir_count,
report.coverage.unregistered_count_is_lower_bound
),
"at least 2"
);
assert_eq!(storage_directory_count_label(2, false), "2");
}

#[test]
fn partial_sizes_render_as_lower_bounds_not_exact_totals() {
let label = storage_size_label(
0,
StorageReportSizeCoverageV1 {
state: StorageReportSizeStateV1::Partial,
reason: Some(StorageReportSizeReasonV1::EntryBudgetExceeded),
unavailable_entry_count: 0,
},
);

assert!(label.starts_with("at least "));
assert!(label.ends_with("(partial)"));
assert!(!label.contains("total"));
}

#[test]
fn page_aggregation_preserves_a_budget_reason_and_unavailable_count() {
let merged = StorageReportSizeCoverageV1 {
state: StorageReportSizeStateV1::Partial,
reason: Some(StorageReportSizeReasonV1::UnreadableEntries),
unavailable_entry_count: 1,
}
.merge(StorageReportSizeCoverageV1 {
state: StorageReportSizeStateV1::Partial,
reason: Some(StorageReportSizeReasonV1::EntryBudgetExceeded),
unavailable_entry_count: 0,
});

assert_eq!(
merged.reason,
Some(StorageReportSizeReasonV1::EntryBudgetExceeded)
);
assert_eq!(merged.unavailable_entry_count, 1);
}
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod reset_project_store_tests {
Expand Down
Loading
Loading