04 — Inventory Specification

From the Burrow™ specification set.

04 — Inventory Specification

Goal

Create an accurate, resumable, explainable catalog of user-approved storage roots without changing user files.

Root Discovery

Discover:

  • internal data volume;
  • iCloud Drive root(s);
  • OneDrive root(s). ~/Library/CloudStorage/OneDrive-<account> and ~/OneDrive are one root when they are the same drive. Separate accounts stay separate. Removing a root from Settings does not delete the folder;
  • mounted external volumes.

Discovery does not imply scanning. The user chooses/approves scan roots.

Default Exclusions

Do not scan operating-system internals by default.

Potential default exclusions include:

  • system directories;
  • application bundles unless specifically requested;
  • node_modules and build/cache directories as configurable exclusions, not hardcoded deletion targets;
  • package internals unless "scan packages" is enabled.

Exclusion rules must be visible and editable.

File Metadata

Capture where available:

  • file record ID;
  • scan ID;
  • volume ID;
  • root ID;
  • full path;
  • parent path;
  • name;
  • extension;
  • content type/UTI;
  • logical size;
  • allocated size (local disk; used for conservative reclaimable-local estimates — never substitute logical placeholder size);
  • creation time;
  • modification time;
  • last access time where reliable;
  • directory flag;
  • symbolic link flag;
  • package/bundle flag;
  • hidden flag;
  • provider;
  • normalized cloud state;
  • provider-specific metadata;
  • filesystem identity;
  • permission/read error state;
  • first seen;
  • last seen.

Symbolic Links

Do not recursively follow symlinks by default. Record the link and target metadata where safe to avoid cycles and unintended traversal.

Packages

Treat macOS packages/bundles as atomic by default for UI purposes. Advanced scanning may inspect contents later.

Progress

A scan should report:

  • current root;
  • files/directories discovered;
  • logical bytes observed;
  • errors;
  • elapsed time;
  • status: queued/running/cancelling/completed/failed/partial.

Cancellation

Cancellation must be safe. Persist completed observations and mark the scan incomplete.

Incremental Behavior

Reuse existing full hashes when the file identity and relevant change indicators prove the content has not changed.

Do not rely only on filename.

Cloud Placeholders

Avoid intentionally downloading cloud-only files merely to inventory them.

If content is unavailable locally, metadata may be cataloged and hash status set to unavailable/deferred.

Normalized cloud_state values match 02 — Storage Model: NOT_CLOUD, CLOUD_UNKNOWN, CLOUD_ONLY, DOWNLOADED, PINNED_LOCAL, SYNCED_LOCAL, ERROR_OR_CONFLICT.

Inventory of CLOUD_ONLY items (engine: CloudPlaceholderInventory) records file_item.logical_size_bytes and file_item.cloud_state. Allocated/reclaimable local bytes use allocated_size_bytes only — never the logical size. Hash status is DEFERRED when the placeholder is catalogable and UNAVAILABLE when it is inaccessible. No content digest is stored by placeholder inventory. Migration 5 creates file_hash; cloud-only rows stay unhashed. An inaccessible placeholder is recorded on that item; the rest of the pass continues.

Inventory has no content-materialization hook. Do not call ubiquity download, Files On-Demand, or Graph APIs to catalog a placeholder. Automated tests use in-memory mocks or disposable temp stubs, never the owner's live iCloud Drive or OneDrive.

Errors

Permission-denied, unavailable-volume, inaccessible-placeholder, path-race, and I/O errors should be recorded per item/root rather than aborting the entire scan.

Persisted kinds use permission-denied, unavailable-volume, inaccessible-placeholder, path-race, and io. Coverage is queryable after the scan via Catalog.scanCoverage(scanId:) (scan.status, scan.error_count, incomplete scan_root rows, scan_error rows). ActivityService records scan-started, scan-completed or scan-cancelled, and each scan-error in memory when a walker is given an activity log. Query those rows with ActivityService.query(scanId:). There is no hosted filesystem API and no HTTP scan-error or activity route in 0.1. The public Activity view uses fixture events.

Scan Completeness

The UI must indicate partial coverage. "No duplicates found" is not valid if significant roots were inaccessible. When a UI/HTTP scan surface exists, it must expose error count and incomplete roots from scanCoverage. Until then the engine catalog query is the 0.1 surface.

Storage analytics after a scan is Catalog.storageAnalytics(). It tags results with scan timestamp/age, uses allocated size for local reclaim, and does not invent a duplicate-recovery total (0.2). There is no HTTP analytics route.

Read-Only Acceptance Test

During v0.1, compare filesystem metadata before and after representative scans and verify Burrow™ has not mutated test files.