From eff07df3887b7021b2a8736e2e51c656b9e41429 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:08:44 +0900
Subject: [PATCH 01/86] feat: surface pending iCloud provider indexing on
current main
Reapply the #247 unique delta (post-#213 lineage) onto current main:
- pending iCloud File Provider indexing detection and admission blocker duration
- backend-clock stall timing with restart-safe preservation
- Naruon readiness indexing-blocker binding and numeric disk-full markers
- preview headroom bound to destination; impossible stall onset rejected
---
CHANGELOG.md | 24 ++
.../adr/0001-cloud-offload-goal-state.md | 206 ++++++++-
.../0006-redacted-icloud-health-evidence.md | 46 ++
.../goals/cloud-offload-goal.json | 5 +-
docs/product-technical-gap-baseline.md | 363 +++++++++++++++-
src-tauri/src/cloud.rs | 93 +++-
src-tauri/src/commands.rs | 63 ++-
src-tauri/src/icloud_sync_health.rs | 173 +++++++-
src-tauri/src/lib.rs | 2 +
src-tauri/src/naruon_cloud_copy_readiness.rs | 29 +-
src-tauri/src/provider_evidence.rs | 9 +-
src-tauri/src/provider_global_sync.rs | 408 +++++++++++++++++-
src-tauri/src/provider_sync.rs | 15 +
...loud_copy_headroom_destination_contract.rs | 17 +
.../naruon_active_fileprovider_transfer.rs | 16 +-
.../naruon_readiness_global_sync_identity.rs | 2 +
...vider_global_sync_clear_state_integrity.rs | 2 +
...der_global_sync_disk_full_code_boundary.rs | 29 +-
src/lib/CloudArchive.svelte | 54 ++-
src/lib/api.ts | 5 +
src/lib/cloudArchiveAdmissionContract.test.ts | 19 +
src/lib/cloudArchiveHealthTiming.test.ts | 24 ++
src/lib/cloudArchiveHealthTiming.ts | 19 +
...cloudOffloadGoalProjectionContract.test.ts | 1 +
24 files changed, 1546 insertions(+), 78 deletions(-)
create mode 100644 src/lib/cloudArchiveHealthTiming.test.ts
create mode 100644 src/lib/cloudArchiveHealthTiming.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32f31fd54..c22d2c9ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -31,6 +31,19 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
- Show the last read-only iCloud File Provider evidence timestamp beside the
new-copy admission state, so a stalled `no progress`/`hard expired` queue has
an actionable retry context without exposing provider paths.
+- Include the redacted iCloud File Provider `pending-indexable-count` in admission evidence and
+ surface `icloud-file-provider-indexing-pending` when Finder remains in “복사 준비 중”.
+- Record the redacted File Provider `disk import: yes` marker as
+ `icloud-file-provider-disk-import-active`, show it beside the iCloud admission evidence, and
+ keep Finder copy, attestation, and source cleanup blocked while macOS is importing a provider
+ disk.
+- Persist the earliest retained timestamp for an unchanged iCloud admission-blocker set, so a
+ restart cannot reset the stalled-copy duration; this diagnostic never grants copy, attestation,
+ or eviction authority.
+- Persist bounded, path-free OneDrive/Google Drive provider-global observations and return
+ `admission_blocked_since_ms` for an unchanged blocker cohort, so a Finder “복사 준비 중” stall
+ remains visible across DiskSage or system restarts; tampered evidence is ignored and the journal
+ never grants copy, attestation, or source-eviction authority.
- Bind Tauri packaging to a fail-closed cross-manifest release-version verifier so `package.json`, `Cargo.toml`, `tauri.conf.json`, and any `v*` release tag must agree on one valid Semantic Version before a bundle is built.
- Add retry-safe release concurrency: fresh first attempts may supersede stale runs, while explicit GitHub rerun attempts do not self-cancel inside the same concurrency group.
- Replace generator-era Cargo package metadata with the DiskSage product description, MIT license expression, canonical source repository URL, and `publish = false` registry-publication boundary; deliberately omit Cargo's deprecated `authors` field, verify publication refusal through Cargo's versioned parsed metadata rather than substring matching, and regression-test commented/out-of-table decoys together with the retained acquisition metadata and doctoring evidence.
@@ -44,9 +57,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
### Fixed
+- Consume the persisted iCloud `admission_blocked_since_ms` diagnostic in the UI stall clock, so a
+ system or application restart preserves the visible duration of an unchanged provider block;
+ durable evidence remains advisory and fail-closed.
+
- Keep the shipped Naruon readiness verifier source includable by its integration boundary test;
the terminal parser contract now compiles in both the binary and test-module contexts.
+- Bound numeric File Provider disk-full markers for `errno`, `odresult_errno`, and
+ `OSStatus -34`, so longer codes such as `errno 280` cannot be misclassified as
+ local-disk-full evidence.
+
+- Include `icloud-file-provider-indexing-pending` in the Naruon iCloud admission-blocker binding,
+ so a signed non-iCloud envelope cannot smuggle that provider blocker through validation.
+
- Cover the `sensitive-config` archive-kind wire label in the generated cloud-plan implementation,
so the macOS/Linux/Windows cloud-plan binaries compile after the sensitive-config safety
boundary is enabled.
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index cfc9a10ce..8211ad5af 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -529,12 +529,50 @@ new-copy admission blocker (`icloud-file-provider-filename-excluded` or
The Finder preparation dialog therefore remains an incomplete provider operation, not a successful
copy receipt, and copy, attestation, and eviction stay fail-closed until the provider is quiet.
-## Amendment: keep the readiness verifier boundary testable (2026-08-22)
-
-The shipped Naruon readiness verifier uses a plain source comment rather than a crate-inner doc
-comment so the same parser can be included by its integration boundary test module. This is a
-compile-boundary repair only; the verifier's path-redacted output and readiness authority do not
-change.
+## Amendment: current iCloud indexing and transfer receipt (2026-08-21 22:22 +0900)
+
+A fresh bounded read-only `fileproviderctl` observation completed at `2026-08-21 22:22:53 +0900`.
+The path-free aggregate reported `needs-indexing=no`, `pending-indexable-count=13,737`, a
+12,449-entry reconciliation backlog, one active upload marker, one active download marker with
+99.16% observed progress, one no-progress fetch, and 18 filename plus 2 root sync exclusions.
+The parser retained `icloud-file-provider-indexing-pending`, transfer, no-progress, and exclusion
+admission blockers; the bounded dump was truncated and no mutation was performed. This is still
+provider-sync-incomplete evidence: the Finder preparation dialog is not a completed copy receipt,
+and native copy, attestation, and eviction remain blocked.
+
+## Amendment: bounded planning and attestation-retention edge cases (2026-08-21)
+
+Exact-content duplicate clusters are now computed before the presentation `limit` is applied. A
+duplicate pair split across that limit therefore still marks the visible member for canonical
+selection and remains represented in the path-free cluster summary; the limit controls presentation,
+not safety evidence. The retention pass also protects the just-written immutable provider record
+when its clock is older than the existing bounded history, so clock regression cannot delete the
+attestation that was just persisted. Both boundaries remain fail-closed and are covered by focused
+Rust regression tests. The local implementation is `c5aa3a1`; its protected-branch publication is
+still pending the repository ruleset's normal PR workflow.
+
+## Amendment: iCloud indexing backlog is an admission blocker (2026-08-21)
+
+A repeated read-only File Provider observation remained unchanged for 21 seconds: the provider
+reported `pending-indexable-count=12474` and upload progress `0/5038` at `0.0000`, alongside the
+existing 18 filename and 2 root exclusions. DiskSage now retains this aggregate count in the
+path-free activity evidence and adds `icloud-file-provider-indexing-pending` to new-copy blockers;
+the UI includes it in the stable-block fingerprint and warns that Finder may remain in “복사 준비
+중”. No Finder/provider process is killed and no cloud or source mutation is performed. The
+extension remains additive to activity schema v3 and is covered by a parser regression test.
+
+## Amendment: restart-safe iCloud admission duration (2026-08-21)
+
+Each successful iCloud health inspection now persists a bounded, path-free observation before
+deriving `admission_blocked_since_ms` from the earliest contiguous retained record with the same
+admission-blocker set. Invalid, unreadable, or changed historical evidence stops the walk, so a
+restart cannot manufacture a longer stall interval. The field is diagnostic only: provider-native
+completion, copy receipts, attestation, and local eviction remain independent fail-closed gates.
+The implementation and regression test are at source head `ad850e9`.
+
+The Naruon readiness allow-list now includes `icloud-file-provider-indexing-pending`, keeping the
+provider-derived blocker set closed under export and rejecting the same blocker on non-iCloud
+envelopes. The binding repair is at source head `6f95ca3`.
## Amendment: bound active-use probes without touching provider state (2026-08-22)
@@ -546,3 +584,159 @@ File Provider databases, cloud objects, and user files remain outside the mutati
focused Rust regression test passed 3/3. A timeout remains incomplete active-use evidence and
keeps cache cleanup and cloud eviction fail-closed; this process-group cleanup is not a provider
recovery or copy-cancellation operation.
+
+## Amendment: keep the readiness verifier boundary testable (2026-08-22)
+
+The shipped Naruon readiness verifier uses a plain source comment rather than a crate-inner doc
+comment so the same parser can be included by its integration boundary test module. This is a
+compile-boundary repair only; the verifier's path-redacted output and readiness authority do not
+change.
+
+## Amendment: exact numeric disk-full markers (2026-08-21)
+
+Provider-global File Provider parsing now applies numeric-boundary matching to `errno 28`,
+`odresult_errno 28`, and `OSStatus -34` in addition to the existing `code=28` forms. Longer
+values such as `errno 280` and `OSStatus -340` remain ordinary provider errors and cannot create
+the actionable local-disk-full blocker. The focused regression covers both exact and extended
+markers; this parser remains diagnostic evidence only and does not grant copy, attestation, or
+eviction authority.
+
+## Amendment: live Finder preparation stall receipt (2026-08-21 23:30 +0900)
+
+The exact-head headless iCloud health probe completed a bounded read-only observation with complete
+evidence. macOS reported `needs-sync-up` and `needs-sync-down`; File Provider reported one
+no-progress fetch, active upload/download progress (`953100`/`988500` millionths), 17,547 pending
+indexable items, 18 filename exclusions, and two root exclusions. The CloudDocs upload queue retained
+six items blocked on sync-up. New-copy admission is therefore `blocked`. These aggregate facts explain
+why Finder can remain at “복사 준비 중”, but they do not identify or attest the seven displayed items.
+
+DiskSage records only bounded, path-free counters and exposes the existing Finder cancellation
+request. It does not kill `fileproviderd`, `bird`, or Finder, modify CloudDocs/provider state, or
+convert this observation into copy, attestation, or eviction authority. A complete quiet observation
+and independent per-item provider evidence remain required. This evidence was observed while PR #246
+was at `fc9f4a4c465fc5ef355f7fbf552ff4295cf4f609` and PR #247 at
+`45214018dff43c6ba7c71253bc50e8c0eab0e1bd`; hosted checks remain authoritative.
+
+## Amendment: detect macOS File Provider disk import during Finder preparation (2026-08-22)
+
+A bounded read-only iCloud File Provider dump can report `disk import: yes` while Finder remains
+in “복사 준비 중”. DiskSage now retains only the boolean aggregate as the
+`icloud-file-provider-disk-import-active` notice, derives the same new-copy admission blocker,
+and surfaces it with the existing fixed Finder-cancel action. The notice contains no path,
+filename, item identifier, or raw provider output. Disk import is provider-progress evidence, not
+a copy receipt; copy, attestation, and source eviction remain fail-closed until a complete quiet
+observation and independent per-item evidence exist.
+
+## Amendment: isolate third-party stall clocks and preserve prior onset on journal faults (2026-08-24)
+
+One shared path-free `provider-global-sync-evidence` journal can contain interleaved OneDrive and
+Google Drive observations. The restart-safe onset walk therefore ignores valid records belonging to
+another provider instead of treating them as a blocker transition. Read, parse, integrity, or
+incomplete-record failures stop the walk while retaining the onset already accumulated from newer
+valid records; they can never manufacture a longer duration. Rust regressions cover both an
+interleaved provider record and a malformed older record. This is diagnostic continuity only: it
+does not grant copy, attestation, cloud mutation, or source eviction authority.
+
+## Amendment: unchanged iCloud preparation queue after restart (2026-08-22 04:05 +0900)
+
+A subsequent bounded, read-only observation found the same iCloud File Provider aggregate state:
+`pending-indexable-count=31,024`, upload progress `5,202,024,494/5,462,125,152` (95.24%),
+download progress `0/828`, and `disk import: yes`. The native sync summary still reported
+`needs-sync-up`/`needs-sync-down` with the last sync at `2026-08-21 20:20:10.166 +0900`; multiple
+items remained in `pending-scan` for roughly three or more hours. The unchanged counters are
+provider-stall evidence, not a per-item receipt and not proof that the visible Finder operation
+completed. DiskSage performed no Finder cancellation, daemon restart, CloudDocs/provider-database
+write, cloud mutation, materialization, or source mutation. New copy, attestation, and eviction
+therefore remain fail-closed; the existing bounded Finder-cancel action remains operator initiated.
+
+## Amendment: current protected PR inventory is evidence-bound (2026-08-22 04:23 +0900)
+
+The product baseline records the exact protected PR queue at DiskSage head `dac324d` (PR #247).
+That inventory is operational evidence only: each row binds its own head SHA, and a later push
+invalidates predecessor checks and approvals. A clean mergeable flag, bot comment, or queued review
+never authorizes a cloud copy, provider attestation, source eviction, or protected merge. The
+review loop remains exact-head review → repair → checks → qualifying approval → normal protected
+merge; no provider, Finder, cloud, or user-file mutation was performed for this amendment.
+
+## Amendment: preserve third-party Finder-stall duration across restart (2026-08-24)
+
+The screenshot-level symptom “복사 준비 중” can outlive both Finder and DiskSage. The existing
+third-party provider-global probe now stamps each bounded OneDrive/Google Drive observation and
+persists a path-free `ProviderGlobalSyncEvidenceSnapshot` under
+`provider-global-sync-evidence`. Create-only, SHA-256-fingerprinted records are capped at 64 KiB,
+stored as `0400` files in a `0700` directory, and retained at most 128 records. Invalid, tampered,
+incomplete, or unsafe records cannot extend a blocker interval.
+
+When the same provider, state, aggregate transfer/indexing flags, and stable blocker set are seen
+again, the command returns `admission_blocked_since_ms`; CloudArchive uses it as the third-party
+stall-clock origin after an application or system restart. The persisted clock is diagnostic only:
+it does not cancel Finder, restart a provider, write cloud data, attest an item, or authorize source
+eviction. A provider-global `-1004`/disconnect, active transfer, reconciliation backlog, or local
+disk-full marker therefore remains a visible fail-closed blocker until a fresh complete quiet
+observation and independent per-item evidence exist.
+
+## Amendment: executed iCloud admission evidence for the current Finder stall (2026-08-24 12:39 +0900)
+
+The exact-head `disksage-icloud-sync-health` binary completed a read-only CloudDocs/WAL snapshot
+with complete evidence. It observed `needs-sync-up|needs-sync-down`, 343 uploads blocked on
+sync-up, one active upload at 95.24%, one active download, and 58,183 pending indexable items.
+New-copy admission is `blocked` for transfer activity, disk import, indexing backlog, root and
+filename exclusions, and native sync-up/down state. The report explicitly retains
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
+
+This is global provider evidence explaining Finder's “복사 준비 중” state, not a per-item cloud
+receipt. The probe reads a copy-on-write snapshot including WAL files, redacts paths, and never
+writes CloudDocs/provider state. Copy, attestation, and source eviction remain fail-closed until
+quiet provider evidence and independent per-item receipts exist.
+
+## Amendment: reject impossible persisted stall-onset values (2026-08-24 12:51 +0900)
+
+CloudArchive treats the persisted blocker onset as diagnostic input, not trusted authority. It now
+accepts that value only when it is a safe, non-negative integer no later than the backend observation;
+negative, future, non-finite, and unsafe-integer values fall back to the current observation. This
+prevents malformed history from suppressing a prolonged Finder “복사 준비 중” warning while keeping
+copy, attestation, cloud-write, and source-eviction authority fail-closed.
+
+## Amendment: latest iCloud preparation queue remains blocked (2026-08-24 12:58 +0900)
+
+The latest exact-head read-only probe still reports `new_copy_admission_state=blocked` and
+`mutation_performed=false`. Native iCloud remains `needs-sync-up|needs-sync-down`; 343 uploads are
+blocked on sync-up, one upload and one download are active, and pending indexable items increased
+to 64,969 from 58,183 at 12:39. Disk import, transfer activity, and the filename/root exclusions
+remain present. This aggregate provider evidence explains the Finder preparation stall but is not a
+per-item receipt, so copy, attestation, cloud-write, and source-eviction authority remain
+fail-closed; DiskSage performs no Finder, provider, source, or cloud mutation.
+
+## Amendment: iCloud indexing backlog increased during the Finder stall (2026-08-24 13:04 +0900)
+
+A subsequent exact-head read-only probe observed the same `needs-sync-up|needs-sync-down` native
+state, 343 uploads blocked on sync-up, one active upload at 95.24%, one active download, and
+`new_copy_admission_state=blocked`. FileProvider pending indexable items increased from 64,969 to
+67,017 while disk import, transfer activity, and the filename/root exclusions remained present.
+The evidence is aggregate and `provider_sync_attested=false`, `local_eviction_authorized=false`,
+and `mutation_performed=false`; therefore it cannot attest the seven Finder items or authorize any
+copy, cloud write, or source eviction.
+
+## Amendment: iCloud backlog continues to grow without a DiskSage database handle (2026-08-24 13:12 +0900)
+
+The next exact-head read-only probe observed `pending_indexable_count=74,946` (up from 67,017),
+the same native `needs-sync-up|needs-sync-down` state, 343 uploads blocked on sync-up, one active
+upload at 95.24%, one active download, and `new_copy_admission_state=blocked`. Finder's
+`real_datasets` destination remained 512 bytes with the same 2026-08-20 03:28:07 mtime and the
+root had about 99 GiB available. Bounded process inspection found `fileproviderd` using 72–129% CPU,
+but no DiskSage process, CloudDocs database, or source path was open in that process. This supports
+a provider-side reconciliation/indexing backlog, not a proven DiskSage database lock. The probe
+still reports `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+`mutation_performed=false`; no Finder, provider, source, or cloud mutation is authorized.
+
+## Amendment: preview headroom follows the destination staging filesystem (2026-08-24 13:22 +0900)
+
+The planner previously exposed source-volume pressure while the native mutation gate correctly
+probed the destination staging ancestor. That could make a cross-volume preview disagree with the
+actual copy boundary. The planner now performs the same bounded destination probe for each visible,
+otherwise-unblocked candidate and emits `local-volume-headroom-insufficient` or
+`local-volume-headroom-unverified`; the native UI gate follows those notices, while explicit
+provider-API uploads remain a separate path. The source-volume snapshot remains diagnostic only.
+Pinned Rust tests, the destination-headroom contract, the full frontend suite (134 tests),
+`svelte-check`, and frontend coverage all pass; mutation, attestation, and eviction authority are
+unchanged and still fail closed.
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 3542e0c15..40d6f28bf 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -38,12 +38,19 @@ The timestamped records are the third evidence stream alongside `volume-pressure
`provider-client-runtime-evidence`. iCloud plans combine the three records with the bounded
freshness comparator in [ADR-0007](0007-pre-copy-evidence-cohort.md); a missing, incomplete,
malformed, or skewed stream remains blocked without reconstructing a provider dump.
+After the current observation is written, the command returns the earliest retained timestamp for
+the same admission-blocker set as `admission_blocked_since_ms`. The UI uses that diagnostic value
+when starting its stall clock, falling back to the current observation only when durable evidence
+is unavailable. This preserves a visible stall duration across an application or system restart;
+it never changes copy, attestation, or eviction authority.
## Consequences
### Positive
- The current iCloud incident remains comparable after a restart or UI refresh.
+- A restarted UI retains the provider stall duration when the bounded evidence journal is readable,
+ instead of presenting a long-running Finder preparation as a newly observed block.
- Provider evidence is durable without copying private provider databases or raw output.
- Bounded create-only records preserve provenance and fail closed on malformed claims.
- The UI can tell the operator when current evidence was observed and when durable comparison failed.
@@ -69,3 +76,42 @@ malformed, or skewed stream remains blocked without reconstructing a provider du
- [ADR-0001](0001-cloud-offload-goal-state.md) — provider evidence and fail-closed eviction gates.
- [ADR-0005](0005-hourly-agent-loop-is-advisory.md) — scheduled loops remain advisory and cannot
authorize mutation.
+
+## Operational evidence update — 2026-08-24
+
+The post-restart bounded observation recorded `pending-indexable-count=32377`, a `28123`-entry
+reconciliation queue, upload progress `6229217391/6540678102`, `scheduling state: running`,
+`disk import: yes`, and `stream reset: yes`; `brctl` still reported `needs-sync-up|needs-sync-down`.
+These aggregate values are incident evidence only. They do not identify a `real_datasets` item or
+prove a cloud write, so the existing decision continues to require per-item provider evidence and
+keeps copy, attestation, and source eviction fail-closed.
+
+The same bounded observation also captured File Provider activity while the Finder dialog remained
+at “preparing to copy” for hours: iCloud continued redacted item ingestion, while a separate
+Google Drive File Provider request returned `NSFileProviderErrorDomain -1004` (device cannot
+connect to the server) during root materialization. The provider name is therefore part of the
+diagnosis; a Finder progress window alone cannot tell which provider is stalled. DiskSage records
+this as provider-specific runtime evidence, exposes the existing explicit Finder-cancel action,
+and never infers copy completion or grants eviction authority from the dialog.
+
+## Operational evidence update — 2026-08-24 11:34
+
+A later bounded read-only observation increased the aggregate iCloud queue to
+`pending-indexable-count=39404` and `reconciliation=35150` while the same upload counter remained
+at `6229217391/6540678102` (95.24%), with `scheduling state: running`, `disk import: yes`, and
+`stream reset: yes`. `brctl` still reported `needs-sync-up|needs-sync-down` and pending scans were
+about 55 hours old. This worsening aggregate state reinforces the existing fail-closed decision;
+it still does not bind the Finder `real_datasets` dialog to an item-level cloud write.
+
+## Operational evidence update — 2026-08-24 13:53
+
+A bounded local recheck at `13:48:21 +0900` found about 96 GiB free on the root volume while Finder,
+`fileproviderd`, and `bird` had remained alive for roughly three hours. The visible `real_datasets`
+target remained 512 bytes with mtime `2026-08-20 03:28:07 +0900`; no target handle appeared in the
+bounded process-handle sample. The latest complete iCloud health receipt available for this loop
+reported 343 uploads blocked on sync-up, one active upload at 95.24%, one active download, and
+74,946 pending indexable items. These facts are aggregate provider evidence, not per-item cloud
+attestation. The decision therefore remains unchanged: DiskSage reports the reconciliation/indexing
+backlog, offers only the explicit bounded Finder-cancel action, and keeps copy, attestation, and
+source eviction fail-closed. No provider process, CloudDocs database, source, or cloud object was
+mutated.
diff --git a/docs/architecture/goals/cloud-offload-goal.json b/docs/architecture/goals/cloud-offload-goal.json
index a10ea466b..acef23059 100644
--- a/docs/architecture/goals/cloud-offload-goal.json
+++ b/docs/architecture/goals/cloud-offload-goal.json
@@ -23,9 +23,10 @@
"pre_copy_evidence_streams": [
"volume-pressure-evidence",
"provider-client-runtime-evidence",
- "icloud-sync-health-evidence"
+ "icloud-sync-health-evidence",
+ "provider-global-sync-evidence"
],
- "pre_copy_evidence_rule": "compare one canonical three-stream cohort; missing, malformed, incomplete, or skewed evidence remains blocked",
+ "pre_copy_evidence_rule": "compare one canonical evidence cohort; missing, malformed, incomplete, or skewed evidence remains blocked",
"pre_copy_evidence_max_skew_ms": 300000,
"runtime_evidence_failure_policy": "fail-closed; unavailable provider-client runtime evidence is not process absence",
"operator_actions": [
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 3091870aa..01c840b6b 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,9 +1,9 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-22 (Asia/Seoul)
-**Repository heads at snapshot:** PR #213 `a6ec6e2`, PR #247 `a0fa7bc`, PR #246 `741ab30`,
-supporting PR #156 `39a08a7`, and PR #192 `30ceea2`; hosted checks and protected review remain
-authoritative, and no merge is claimed from queued or stale status.
+**Snapshot:** 2026-08-24 13:58 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 13:58 correction below supersede earlier
+historical captures; hosted checks and protected review remain authoritative, and no merge is
+claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
**Evidence rule:** this document is a dated baseline, not an authority for transfer or deletion. Runtime receipts, provider attestations, object identity, and current GitHub checks remain authoritative.
@@ -15,12 +15,36 @@ authoritative, and no merge is claimed from queued or stale status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
+## 2026-08-22 04:23 +0900 current protected PR inventory
+
+This is the current review queue captured from GitHub immediately before this snapshot. A commit
+SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
+checks and approvals.
+
+| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
+| --- | --- | --- | --- | --- | --- |
+| #249 | `44390608d30417477f6a66601b18a53ca87b0a9c` | yes | blocked | review required | Git worktree audit help; Strix provider-prefix failure is tracked against central `.github` PR #1263 |
+| #247 | `7c690c09a8409f5aafdc880d188fcc2939c14ce3` | no | dirty | review required | destination-staging headroom binding and provider-scoped stall-history repair; hosted checks/review rerunning |
+| #246 | `476678c150ded97b400d62566292adfff56a84c2` | no | clean | none | Storybook/accessibility contract; approvals still absent |
+| #244 | `b9941295ac354bb63cf911a064a1f4df1f8eb60b` | yes | blocked | review required | Rust 1.97.1 baseline; protected approval quorum absent |
+| #238 | `d44b23bdf4108bf6b6f6378f7e0ac305187deec6` | no | blocked | review required | mail-parser update; all current checks are green but protected approvals are absent |
+| #234 | `22bc81585257f409abf9f99a5db81184e84dafe9` | yes | blocked | review required | ureq update; protected approval quorum absent |
+| #232 | `99db1d36f722aeb00b280793126064e4951e62be` | no | blocked | review required | @types/node update; protected approval quorum absent |
+| #230 | `c7e4e623e9b691dcd6a24cad3cc492393cb5d83e` | yes | blocked | review required | download-artifact update; protected approval quorum absent |
+| #228 | `1eb947ec9d4e591638230a8cb24af4d5b14ae35b` | yes | clean | none | private-evidence identity hardening; review pending |
+| #189 | `1ada64a334fc27a022d42c897fabe32ccc25ae7e` | no | blocked | review required | stacked Homebrew/iCloud safety UI repair; hosted checks are rerunning |
+| #156 | `c6dc8a6635639329c9bb02d9e32d6548abeaa427` | yes | unknown | review required | exact-head coverage/release contracts; coverage measured below 100% |
+
+No protected merge is inferred from `clean`, green predecessor checks, bot comments, or queued
+reviews. The queue is processed exact-head-first: review, repair, recheck, then normal protected
+merge.
+
## Buyer-observable product gaps
| Priority | Gap / observable symptom | Evidence | Acceptance criterion |
| --- | --- | --- | --- |
-| P0 | Cloud offload can remain blocked while a provider is syncing or reports `local-current`/`is_uploaded=false`; the user sees no safe reclaim despite free cloud capacity. | Existing provider-global and iCloud native-state gates; `bird`/`fileproviderd` remain active during the current incident, with about 3.8 GiB available at the latest observation. | UI explains the exact blocker, last evidence time, and next bounded retry; a verified provider attestation alone can advance a candidate, never a stale projection. |
-| P0 | A long Finder/provider copy can appear hung and consume the remaining local headroom. | The `real_datasets` Finder copy remained at “준비 중” for hours; the latest bounded iCloud dump retained 125 no-progress fetch/create markers, a 95.24% upload, and a zero-progress 1.06GB download while scheduling was `running`. Bounded `/bin/cp`/`mkdir` and global probes use private process groups and headroom gates. | Preview shows required bytes + staging reserve; timeout cleans only the child-created destination and leaves a durable receipt. |
+| P0 | Cloud offload can remain blocked while a provider is syncing or reports `local-current`/`is_uploaded=false`; the user sees no safe reclaim despite free cloud capacity. | Existing provider-global and iCloud native-state gates; `bird`/`fileproviderd` remain active during the current incident, while the root volume has about 96 GiB available. | UI explains the exact blocker, last evidence time, and next bounded retry; a verified provider attestation alone can advance a candidate, never a stale projection. |
+| P0 | A long Finder/provider copy can appear hung and consume the remaining local headroom. | A repeated exact-head iCloud dump remained unchanged for 21 seconds with `pending-indexable-count=12474`, upload `0/5038` at `0.0000`, active upload/download markers, and 18 filename plus 2 root exclusions. | UI reports the indexing backlog and stable blocker duration; Finder copy, attestation, and eviction remain fail-closed until a fresh quiet provider observation. |
| P1 | Personal desktop-client capacity is not the same as API quota; OAuth is unnecessarily implied for a single-user installation. | ADR-0001 permits copy-only desktop-client mode marked `capacity-unverified`; the cloud connection UI defaults to read-only OAuth consent and requires an explicit write-access opt-in. | Settings clearly distinguish local desktop client, API quota, and organization OAuth; no OAuth prompt is required for the local-only path. |
| P1 | Users cannot yet see a full lineage graph connecting source, metadata, archive member, provider item, receipt, Goal, and eviction decision. | The candidate UI now exposes a compact source→metadata→archive→provider lineage panel using the stable fingerprint, confidence, and blocker state; provider item/receipt/permit remain explicitly pending until their evidence exists. | Export and UI show stable content IDs, provenance edges, confidence, and blockers without exposing raw private paths. |
| P1 | “Orphan”/duplicate cleanup is difficult to trust because relationship evidence is not visible before action. | Ontology and duplicate/orphan PRs are open; current default path remains fail-closed. | Every proposed removal has an explainable parent/child/duplicate relation, identity recheck, reversible Trash action, and a no-candidate result when evidence is incomplete. |
@@ -31,10 +55,11 @@ authoritative, and no merge is claimed from queued or stale status.
| Priority | Gap | Current state | Smallest next proof |
| --- | --- | --- | --- |
| P0 | Provider end-to-end receipt is absent for the current iCloud incident. | Global probe can time out and CloudDocs state is intentionally not force-killed or deleted; the native copy boundary now requires an integrity-checked three-stream pre-copy cohort before mutation. | Capture a bounded fresh provider evidence receipt after sync settles; keep transfer/eviction disabled until it is complete. |
-| P0 | Disk pressure telemetry and provider queue evidence must remain comparable across loops without retaining raw provider output. | Cloud plans and explicit iCloud health refreshes persist bounded, path-free `LocalVolumeSnapshot`, `ProviderClientRuntimeSnapshot`, and `IcloudSyncHealthEvidenceSnapshot` records under `volume-pressure-evidence`, `provider-client-runtime-evidence`, and `icloud-sync-health-evidence`; iCloud plans now combine them into a timestamp/fingerprint-bound cohort. | Missing, incomplete, malformed, or more-than-five-minute-skewed cohort observations remain blocked; a fresh exact-head native incident plan is still needed to compare the emitted cohort with the live incident. |
+| P0 | Disk pressure telemetry and provider queue evidence must remain comparable across loops without retaining raw provider output. | Cloud plans and explicit provider health refreshes persist bounded, path-free `LocalVolumeSnapshot`, `ProviderClientRuntimeSnapshot`, `IcloudSyncHealthEvidenceSnapshot`, and `ProviderGlobalSyncEvidenceSnapshot` records under `volume-pressure-evidence`, `provider-client-runtime-evidence`, `icloud-sync-health-evidence`, and `provider-global-sync-evidence`; iCloud plans combine their three-stream cohort, while third-party plans retain the provider-global stream for restart-safe blocker duration. | Missing, incomplete, malformed, or more-than-five-minute-skewed iCloud cohort observations remain blocked; third-party provider-global history can only extend a matching diagnostic clock and never grants copy, attestation, or eviction authority. |
| P1 | Hourly product-development/review loop is not yet live in this repository environment. | The repository-local `.github/workflows/hourly-product-loop.yml` is intentionally `workflow_dispatch`-only because its direct contextual-orchestrator HTTP call is advisory and not a pinned OpenCode worker. The trusted central [`disksage-hourly-review-repair.yml`](https://github.com/ContextualWisdomLab/.github/blob/main/.github/workflows/disksage-hourly-review-repair.yml) runs at `37 * * * *` and dispatches the pinned scheduler `a3fdaa1aacaba9443a18573f3c309fe1841fc2f0`, which performs the OpenCode OIDC exchange. The local workflow still uploads a seven-day path-free receipt when manually configured; no external endpoint or deployment receipt is available here. | Verify one central scheduler receipt and one local manual advisory receipt; preserve read-only permissions, exact-head binding, and no provider-secret import or mutation. |
-| P1 | Open PR queue prevents a clean protected release line. | At this loop capture PR #213 is exact head `6f424af` on `feat/provider-sync-dynamic-goals`; its required checks reset after the provider-dump pipe repair and the prior review decision remains stale `CHANGES_REQUESTED`. The orphan cleanup follow-up is PR #245, initially implemented at `3d2406c` and subsequently extended with provider-sync and cleanup-refresh safety fixes. Both remain protected and unmerged pending exact-head review. | Process one PR at a time: current-head review → fix → required checks → fresh approval → normal protected merge; never bypass or self-approve. |
+| P1 | Open PR queue prevents a clean protected release line. | The current exact-head inventory above still has protected review/quorum gaps; PR #189 and #247 have checks running, while PR #238 is green but has no qualifying approval. | Process one PR at a time: current-head review → fix → required checks → fresh approval → normal protected merge; never bypass or self-approve. |
| P1 | Current UI coverage is contract-heavy rather than runtime E2E for native File Provider states. | The UI now displays `로컬 최신본·업로드 미확인` and maps blockers without backend detail; provider operations are not safely reproducible on this full disk. Rust fixtures now cover `local-current + is_uploaded=false`, provider timeout, timeliness transitions, and receipt/evidence invalidation; native runtime E2E remains unavailable while the provider is unhealthy. | Keep the fixture-backed state machine green and add a bounded native E2E receipt only after a quiet provider observation is authoritative. |
+| P1 | Preview headroom could disagree with native mutation headroom on cross-volume layouts. | The mutation boundary already probes the destination staging ancestor, while the old preview/UI gate used the source-volume snapshot. | The planner now probes the destination for every visible unblocked candidate and the native UI follows the resulting insufficient/unverified notices; provider-API upload remains separate. |
| P1 | Ontology/catalog integrations are export boundaries, not deployed services. | Naruon/semantic catalog and Zotero local API docs/contracts exist; no Noema/contextual-orchestrator runtime dependency is required. | Keep integrations optional and path-free; add live service tests only when a concrete consumer and secret boundary exist. |
| P2 | 100% documentation/docstring and edge-case coverage is not yet evidenced. | Existing checks cover core Rust/TS behavior, not a repository-wide percentage claim. | Publish measured coverage per language and close high-risk edge paths before claiming 100%. |
| P2 | Figma design source is not part of the current change. | No visual redesign or Figma artifact was introduced in this baseline. | If a product UI redesign is approved, record the Figma File ID in a new ADR before implementation. |
@@ -165,6 +190,23 @@ authoritative, and no merge is claimed from queued or stale status.
At each scheduled or operator loop, update this file only with new dated evidence: current head, open-PR/check state, provider receipt state, disk headroom, and the smallest acceptance proof completed. Do not convert an incomplete provider probe, filename date, model answer, or GitHub review comment into a transfer or deletion authority.
+## 2026-08-21 23:30 +0900 live iCloud Finder-preparation receipt
+
+- The exact-head `disksage-icloud-sync-health` binary completed a bounded, read-only iCloud
+ observation. The report was `evidence_complete=true`, native status `needs-sync-up` plus
+ `needs-sync-down`, and File Provider activity schema 3 with one no-progress fetch, active
+ upload/download markers at `953100`/`988500` millionths, and `pending_indexable_count=17547`.
+ The upload queue also retained six `blocked_on_sync_up` items; 18 filename exclusions and two
+ root exclusions were observed. New-copy admission is `blocked`; `mutation_performed=false`.
+- `fileproviderctl` also showed active iCloud materialization/fetch jobs and a roughly 14,965-entry
+ reconciliation backlog. This is consistent with the Finder `real_datasets` “복사 준비 중”
+ symptom, but it is not a per-item copy receipt. DiskSage keeps copy, attestation, and source
+ eviction fail-closed until a fresh complete quiet observation and per-item provider evidence
+ exist. No Finder/provider daemon, CloudDocs database, cloud object, or source file was changed.
+- Current exact PR heads are UX #246 `fc9f4a4c465fc5ef355f7fbf552ff4295cf4f609` and provider
+ #247 `45214018dff43c6ba7c71253bc50e8c0eab0e1bd`; their hosted checks remain pending, while
+ local Rust/UX validation is green. The live observation does not authorize a protected merge.
+
## 2026-08-21 lineage graph update
- Source head `677042467b3398866757f39b9475bd0b267abc75` now exports path-free ontology relations for
@@ -655,6 +697,57 @@ At each scheduled or operator loop, update this file only with new dated evidenc
complete; DiskSage keeps copy, attestation, and eviction fail-closed. This separates the active
provider-index backlog from the earlier low-space pressure incident.
+## 2026-08-21 bounded-planning and evidence-retention follow-up
+
+- Exact duplicate detection now runs before the candidate presentation limit, so a duplicate pair
+ split by `limit` still marks the visible candidate for human canonical selection and remains in
+ the path-free cluster summary. A focused Rust regression test covers this boundary.
+- Provider evidence retention protects the just-written record when its timestamp is older than
+ the existing history, preventing clock regression from deleting fresh proof. The retention
+ integration test covers the bounded 128-record history. Local implementation commit `c5aa3a1`
+ is not yet published because the repository ruleset currently rejects direct branch updates.
+
+## 2026-08-21 current exact-head iCloud/indexing and PR audit
+
+- Exact-head local commit `c5edabd` adds the path-free iCloud File Provider
+ `pending_indexable_count` field, emits `icloud-file-provider-indexing-pending`, includes it in
+ Naruon readiness and the stable UI blocker fingerprint, and records the change in ADR-0001,
+ this baseline, and `CHANGELOG.md`. The pinned Rust 1.97.1 parser test passed; `npm run check`
+ reported 0 errors/0 warnings and the CloudArchive contract suite passed 3/3.
+- The rebuilt `disksage-icloud-sync-health` observed at `2026-08-21 21:54:33 +0900` returned
+ `schema_version=5`, complete evidence, native `idle`/`has-synced-down`, File Provider activity
+ schema 3 with pending indexable `12474`, active upload/download `1/1`, and filename/root
+ exclusions `18/2`. Admission remains blocked by upload-in-flight, both exclusion blockers,
+ indexing-pending, and transfer-active; `mutation_performed=false`.
+- A normal push of `c5edabd` was rejected by ruleset `GH013` because branch changes must go through
+ a pull request and the central required workflows are unsatisfied. No bypass, force-push, admin
+ merge, or self-approval was used. Remote PR #213 therefore remains at `108bba0`; the local
+ follow-up is explicitly unprotected until a normal PR path becomes available.
+
+## 2026-08-21 iCloud indexing backlog follow-up
+
+- A repeated read-only iCloud File Provider observation remained unchanged for 21 seconds with
+ `pending-indexable-count=12474`, upload progress `0/5038` at `0.0000`, 18 filename exclusions,
+ and 2 root exclusions. DiskSage now exports the aggregate indexing backlog, blocks new-copy
+ admission with `icloud-file-provider-indexing-pending`, and surfaces the count in the Finder
+ “복사 준비 중” warning. No provider or source mutation was performed.
+
+## 2026-08-21 current iCloud indexing and transfer receipt
+
+- A fresh bounded read-only `fileproviderctl` observation completed at `2026-08-21 22:22:53 +0900`.
+ It reported `needs-indexing=no`, pending indexable `13737`, a `12449`-entry reconciliation
+ backlog, active upload/download markers, one no-progress fetch, and filename/root exclusions
+ `18/2`. The bounded dump was truncated; DiskSage persisted only path-free aggregate evidence and
+ set no-progress, indexing-pending, transfer, and exclusion blockers. `mutation_performed=false`;
+ Finder preparation is not a copy receipt.
+
+## 2026-08-21 provider disk-full marker boundary
+
+- Provider-global sync parsing now treats only the exact numeric markers `errno 28`,
+ `odresult_errno 28`, and `OSStatus -34` as local-disk-full evidence; longer values such as
+ `errno 280` are retained as generic provider errors. The focused boundary regression passes,
+ and no provider, source, or cloud state was mutated.
+
## 2026-08-22 readiness verifier integration boundary
- The Naruon readiness verifier's source comment is now valid both as a standalone binary and when
@@ -679,3 +772,257 @@ At each scheduled or operator loop, update this file only with new dated evidenc
pipe leak that could starve the independent `ps` probe and report a false active-use timeout.
The focused Rust test passed 3/3. The same patch is present on stacked PR heads `a0fa7bc` (#247)
and `741ab30` (#246); hosted checks are rerunning and protected merge/review is still pending.
+
+## 2026-08-22 File Provider disk-import detection
+
+- A fresh bounded `fileproviderctl` observation showed the iCloud domain with Finder enumerators,
+ `disk import: yes`, active upload progress of `5,202,024,494 / 5,462,125,152` bytes, and
+ `pending-indexable-count=30,960`. These aggregate markers explain why Finder can remain in
+ “복사 준비 중”, but they do not bind the operation to `real_datasets` or prove a per-item cloud
+ copy. DiskSage now records the redacted `icloud-file-provider-disk-import-active` notice,
+ projects it into the new-copy admission blockers and Naruon readiness export, and shows it next
+ to the existing fixed Finder-cancel action. Copy, attestation, and source eviction remain
+ fail-closed; no provider process, source, CloudDocs database, or cloud object was mutated.
+
+## 2026-08-22 04:05 +0900 unchanged iCloud preparation queue
+
+- A second read-only host observation found the same bounded aggregate values after the earlier
+ disk-import evidence: `pending-indexable-count=31,024`, upload
+ `5,202,024,494/5,462,125,152` (95.24%), download `0/828`, and `disk import: yes`.
+ `brctl` still reported `needs-sync-up`/`needs-sync-down`, with last sync at
+ `2026-08-21 20:20:10.166 +0900`; many `pending-scan` entries were three or more hours old.
+- This strengthens the product diagnosis of a stalled File Provider preparation queue but does
+ not bind the state to a particular Finder item or prove a cloud copy. DiskSage performed no
+ cancellation, daemon restart, provider-database write, materialization, cloud mutation, or
+ source mutation. The runtime Goal remains `provider-sync-incomplete`; copy, attestation, and
+ source eviction remain blocked until a fresh complete quiet observation and independent
+ per-item evidence exist.
+
+## 2026-08-22 persisted stall-duration wiring
+
+- The current bounded probe at `2026-08-21 20:21:04 +0000` still reports two no-progress fetches,
+ `pending-indexable-count=31882`, unchanged aggregate upload/download counters, and active disk
+ import. The iCloud health command already derives `admission_blocked_since_ms` from the
+ integrity-checked evidence journal, but the frontend previously ignored that field and restarted
+ its 15-minute clock after a UI/system restart.
+- DiskSage now carries the field through the TypeScript report contract and uses it as the UI stall
+ clock origin, with a current-observation fallback only when persistence is unavailable. This
+ makes the screenshot's long-running “복사 준비 중” state remain visible as a stall after restart;
+ it does not cancel Finder, write provider state, or authorize copy, attestation, or eviction.
+
+## 2026-08-22 06:05 +0900 repeated Finder preparation stall
+
+- A new bounded, read-only observation still found four `fetchContentsForItemWithID` requests with
+ no progress, `pending-indexable-count=31882`, active disk import, unchanged aggregate upload
+ (`5205160706/5465661912`) and download (`10647837/11116116`) counters, and `brctl` flags
+ `needs-sync-up|needs-sync-down`. Finder had remained alive for roughly 18 hours and the data
+ volume had only about 4.9 GiB available.
+- The observation confirms provider-level preparation debt but remains aggregate evidence: it does
+ not identify the seven Finder items in `real_datasets` or prove any cloud copy. DiskSage keeps
+ the runtime Goal `provider-sync-incomplete`, copy/attestation/source eviction fail-closed, and
+ exposes only the explicit bounded Finder-cancel action; no Finder/provider process, CloudDocs
+ database, source, or cloud object was mutated.
+
+## 2026-08-24 current File Provider reconciliation backlog
+
+- A fresh bounded, read-only `fileproviderctl` observation after the system restart reported
+ `needs-indexing=no` but `pending-indexable-count=32377`, a `28123`-entry reconciliation queue,
+ upload progress `6229217391/6540678102` (95.24%), `scheduling state: running`, `disk import: yes`,
+ and `stream reset: yes`. `brctl status` still reported `needs-sync-up|needs-sync-down` with the
+ last sync at `2026-08-21 20:20:10.166 +0900`; repeated pending scans were roughly 54 hours old.
+- These are provider-global markers and do not bind to the seven Finder items in `real_datasets` or
+ prove a per-item cloud write. DiskSage therefore keeps Goal `provider-sync-incomplete`, copy,
+ attestation, and source eviction fail-closed, and leaves only the explicit bounded Finder-cancel
+ action available. No Finder/provider process, CloudDocs database, source, or cloud object was
+ mutated by this observation.
+
+## 2026-08-24 11:13 +0900 provider-specific Finder stall follow-up
+
+- A fresh read-only Google Drive File Provider dump was approximately 4.99 MiB, confirming that
+ the provider-wide probe must retain its 32 MiB bounded cap; the product branch already carries
+ that cap and parses `temporarily disconnected`, `NSFileProviderErrorDomain -1004`, active
+ transfer, reconciliation, and item-not-found markers without retaining paths.
+- The live log recorded Google Drive root materialization failures with File Provider error
+ `-1004` (server/device connection unavailable) while iCloud continued redacted item ingestion.
+ This makes the provider identity part of the user diagnosis: a Finder “preparing to copy” dialog
+ is not sufficient evidence of a cloud write and cannot be mapped to `real_datasets` without an
+ item-level receipt.
+- Current exact heads are PR #247 `3e43e0d4d3aa15a7f25161f4107bf3f2c29d261f` and PR #156
+ `25b3e42be7e0e22cafca878ef25383959dd773d6`; both have hosted checks still running/queued and
+ neither has a qualifying protected approval. No process, provider database, source file, or
+ cloud object was mutated. The runtime Goal remains `provider-sync-incomplete` and all copy,
+ attestation, and source-eviction gates remain fail-closed.
+
+## 2026-08-24 11:34 +0900 worsening iCloud preparation queue
+
+- A subsequent bounded read-only iCloud observation increased `pending-indexable-count` to `39404`
+ and reconciliation to `35150`; upload remained `6229217391/6540678102` (95.24%) with scheduling
+ running, `disk import: yes`, and `stream reset: yes`. `brctl` still reports
+ `needs-sync-up|needs-sync-down`, with pending scans about 55 hours old.
+- The aggregate queue is worsening rather than quieting. It remains incident evidence only: it
+ does not identify the seven `real_datasets` items or prove a cloud write. DiskSage keeps the
+ runtime Goal `provider-sync-incomplete`, exposes only the bounded Finder-cancel action, and
+ keeps copy, attestation, and source eviction fail-closed. No provider process, CloudDocs
+ database, source file, or cloud object was mutated.
+
+## 2026-08-24 provider-stall duration persistence
+
+- The provider-global admission report now carries a backend observation timestamp and an optional
+ `admission_blocked_since_ms` value. OneDrive/Google Drive probes persist only bounded, path-free
+ aggregate snapshots with create-only `0400` records, `0700` directory permissions, SHA-256
+ integrity, and 128-record retention; raw File Provider dumps and user paths are not retained.
+- CloudArchive consumes the persisted onset for the same provider/blocker fingerprint. Therefore
+ a Finder “복사 준비 중” dialog that survives a restart is shown as a continuing stall instead of
+ a newly observed five-minute window. Invalid or tampered history falls back to the current
+ observation and remains fail-closed; the feature never cancels Finder or authorizes cloud copy,
+ attestation, or source eviction.
+
+## 2026-08-24 12:16 +0900 exact-head and review repair correction
+
+- PR #247's latest source fix is `7e82b0c` after the provider-global restart-duration
+ implementation. It scopes the persisted stall walk to the observed provider and preserves the
+ onset already accumulated when an older record cannot be read or parsed. The earlier
+ `7fa3f7d...`, `3db3c33...`, `3e43e0d...`, and `2ee31ea...` rows are predecessor evidence; their
+ checks and reviews are stale. The PR remains ready for review with no qualifying approval, and
+ the live PR head/checks must be re-fetched after this documentation publication.
+- Parent PR #213 is ready for review at exact head `0584bcc600e037d564a4ff254b6e8570361d9218`;
+ its hosted coverage/security/release checks are green, but protected review quorum is absent.
+- Local proof for the source fix is Rust provider-global 20/20, provider/readiness
+ integration tests 6/6, frontend Vitest 32 files/133 tests, and `svelte-check` 0 errors/0
+ warnings. These checks do not authorize a protected merge or any Finder/provider/source/cloud
+ mutation.
+
+## 2026-08-24 12:26 +0900 repeated Finder preparation stall
+
+- Finder PID 1422 has been alive for about 1 hour 42 minutes; `fileproviderd` remains active at
+ about 22% CPU while `bird` is present. The `real_datasets` destination remains 14 entries,
+ 512 bytes, and mtime `2026-08-20 03:28:07`, so no destination byte-copy progress was observed.
+- The root volume has about 91 GiB available. `brctl status` still reports iCloud
+ `needs-sync-up|needs-sync-down` with the last sync at `2026-08-21 20:20:10.166 +0900` and
+ repeated pending scans. This is a provider preflight/indexing stall, not local capacity pressure
+ and not proof of a cloud write for the seven Finder items shown in the dialog.
+- DiskSage performed read-only inspection only. It did not cancel Finder, restart/kill provider
+ daemons, modify CloudDocs/provider state, or mutate source/cloud data; Goal
+ `provider-sync-incomplete` and all copy/attestation/eviction gates remain fail-closed.
+
+## 2026-08-24 12:35 +0900 Strix provider-prefix failure in the open-PR queue
+
+- DiskSage PR #249 exact head `44390608d30417477f6a66601b18a53ca87b0a9c` has a failed Strix
+ check. The run reached its configured fallback `openai-direct/gpt-5.6-luna`, but LiteLLM
+ rejected that hyphenated provider prefix (`LLM Provider NOT provided`) before producing a
+ vulnerability report; the required check correctly failed closed rather than treating zero
+ findings as authoritative evidence.
+- The root repair is in the central `.github` PR #1263 exact head
+ `3669bceba9679883d10ffa859eea87bf4705dfd3`: normalize `openai-direct/` to
+ `openai_direct/`, dispatch LiteLLM as `openai/gpt-5.6-luna`, and switch the credential/API-base
+ boundary for cross-provider fallbacks. Its current head has no unresolved review thread, but
+ its protected review decision remains stale `CHANGES_REQUESTED` while the Strix check runs.
+- This is CI-provider infrastructure evidence, not a DiskSage data or Finder mutation. No local,
+ provider, source, or cloud data was changed by the diagnosis.
+
+## 2026-08-24 12:39 +0900 executed DiskSage iCloud admission probe
+
+- The exact product head's `disksage-icloud-sync-health` binary completed a read-only local
+ CloudDocs/WAL snapshot with `evidence_complete=true`, `mutation_performed=false`,
+ `provider_sync_attested=false`, and `local_eviction_authorized=false`. The snapshot is
+ supplementary global evidence; it does not claim a per-item cloud write for `real_datasets`.
+- iCloud reported `needs-sync-up|needs-sync-down`, 343 uploads blocked on sync-up, one active
+ upload at 95.24%, one active download, and `pending_indexable_count=58183`. The admission state
+ is `blocked` with transfer-active, disk-import, indexing-pending, root/filename exclusion, and
+ native sync-up/down blockers. This directly explains why Finder remains in “복사 준비 중”.
+- The probe read SQLite through a copy-on-write snapshot including WAL files, redacted paths, did
+ not write CloudDocs/provider state, and did not cancel Finder or mutate any source/cloud object.
+
+## 2026-08-24 12:15 +0900 current Finder preparation stall observation
+
+- Finder has remained alive since `10:43:49 +0900`, while the visible `real_datasets` operation
+ remains in “복사 준비 중”. The local destination directory's mtime and size stayed unchanged
+ at `2026-08-20 03:28:07` and 512 bytes across bounded checks from `12:14:02` through
+ `12:14:12`; no new destination or temporary file appeared after the incident start window.
+- The root volume had 86 GiB available, so local capacity is not the current blocker. A bounded
+ Finder sample stayed in DesktopServices/FileProvider URL-property and child-synchronization
+ work rather than a byte-copy path. This is evidence of preflight/provider waiting, not a copy
+ receipt and not proof that any of the seven displayed items reached a cloud object.
+- `brctl` still reports `needs-sync-up|needs-sync-down` with the last sync at
+ `2026-08-21 20:20:10.166 +0900`. OneDrive's latest diagnostic reported zero bytes/files
+ queued and no download/upload failures; this does not prove Finder's source selection, so the
+ product keeps the provider identity and item-level receipt separate.
+- DiskSage performed only read-only inspection. It did not cancel Finder, restart or kill
+ `bird`/`fileproviderd`, write a CloudDocs/provider database, or mutate a source or cloud object.
+ Goal `provider-sync-incomplete`, copy/attestation/eviction gates, and the explicit bounded
+ Finder-cancel action remain unchanged.
+
+## 2026-08-24 12:51 +0900 impossible stall-onset values rejected at the UI boundary
+
+CloudArchive now accepts a persisted blocker onset only when it is a safe integer in the observed
+time range. Negative, future, non-finite, or otherwise impossible values fall back to the current
+backend observation instead of producing a negative duration or hiding the 15-minute Finder-stall
+warning. The focused Vitest contract passes four cases and `svelte-check` reports zero diagnostics;
+this diagnostic guard grants no copy, attestation, cloud-write, or source-eviction authority.
+
+## 2026-08-24 12:57 +0900 frontend coverage evidence
+
+The exact product worktree ran all 32 frontend test files (134 tests) successfully. V8 reports
+100% statements (211/211), branches (70/70), functions (83/83), and lines (173/173) for the
+instrumented frontend surface, including the impossible stall-onset contract. This is frontend
+test evidence only; it does not imply repository-wide 100% coverage or provider/cloud authority.
+
+## 2026-08-24 12:58 +0900 iCloud preparation queue remains blocked
+
+The latest exact-head read-only probe still reports `new_copy_admission_state=blocked` and
+`mutation_performed=false`. The native summary remains `needs-sync-up|needs-sync-down`; 343 upload
+items are blocked on sync-up, one upload and one download are active, and FileProvider's pending
+indexable count increased to 64,969 (from 58,183 at 12:39). Disk import, transfer activity, and
+the 28 filename/2 root exclusions remain present. The Finder target is unchanged at 14 entries,
+512 bytes, mtime `2026-08-20 03:28:07 +0900`, with about 101GiB free on `/`. DiskSage therefore
+continues to block new copy, attestation, and source eviction; no Finder/provider/source/cloud
+mutation was performed.
+
+## 2026-08-24 13:04 +0900 iCloud indexing backlog increased
+
+A subsequent exact-head read-only probe observed the same native `needs-sync-up|needs-sync-down`
+state, 343 uploads blocked on sync-up, one active upload at 95.24%, one active download, and
+`new_copy_admission_state=blocked`. FileProvider pending indexable items increased from 64,969 to
+67,017 while disk import, transfer activity, and the 28 filename/2 root exclusions remained
+present. Aggregate evidence still has `provider_sync_attested=false`, `local_eviction_authorized=false`,
+and `mutation_performed=false`; no Finder/provider/source/cloud mutation was performed.
+
+## 2026-08-24 13:12 +0900 iCloud backlog growth and process attribution
+
+The next exact-head read-only probe observed `pending_indexable_count=74,946` (up from 67,017),
+the same native `needs-sync-up|needs-sync-down` state, 343 uploads blocked on sync-up, one active
+upload at 95.24%, one active download, and `new_copy_admission_state=blocked`. Finder's
+`real_datasets` destination remained 512 bytes with mtime `2026-08-20 03:28:07 +0900`; `/` had
+about 99GiB available. Bounded process inspection saw `fileproviderd` at 72–129% CPU but no
+DiskSage process, CloudDocs database, or source path open in it. This is provider-side backlog
+evidence, not proof of a DiskSage database lock; provider attestation, eviction authorization, and
+all mutations remain disabled.
+
+## 2026-08-24 13:12 +0900 live protected PR inventory correction
+
+The earlier 12:51 table is historical. The live GitHub inventory now has PR #247 on `main` at
+`584d0ede1a6fef75b7bfc2191aa3ea47e59b2a66` (open, non-draft, checks pending, review required), PR
+#246 at `476678c150ded97b400d62566292adfff56a84c2` (open, non-draft, all required checks pass but
+no approvals), and the remaining open queue includes #249, #244, #238, #236, #234, #232, #231,
+#230, #228, #227, #225, #223, #222, #220, #218, #217, #216, #215, #214, #212, #209, #208,
+#207, #206, #205, #204, #203, #202, #200, #199, #198, #197, #195, #193, #192, #190, #189,
+#188, #187, #186, #182, #181, #179, #174, #156, #150, and #149. No protected merge is inferred
+from `CLEAN`, green predecessor checks, or bot comments; the live ruleset still requires two
+independent approvals, last-push approval, resolved threads, and normal merge/squash.
+
+## 2026-08-24 13:53 +0900 live Finder/provider follow-up
+
+- A bounded local recheck at `13:48:21 +0900` found about 96 GiB available on `/`. Finder PID 1422,
+ `fileproviderd` PID 1450, and `bird` PID 1462 had all remained alive for roughly 3 hours; the
+ `real_datasets` target was still 512 bytes with mtime `2026-08-20 03:28:07 +0900`. No target
+ handle appeared in the bounded process handle sample; File Provider held only its Mobile Documents
+ root and `bird` held CloudDocs session database shared-memory files.
+- The latest complete DiskSage iCloud health receipt available for this loop (`13:12`) reported
+ `new_copy_admission_state=blocked`, 343 uploads blocked on sync-up, one active upload at 95.24%,
+ one active download, and 74,946 pending indexable items. The evidence is aggregate and does not
+ identify the seven Finder items or prove a cloud write; `provider_sync_attested=false`,
+ `local_eviction_authorized=false`, and `mutation_performed=false` remain explicit.
+- This confirms a File Provider reconciliation/indexing backlog rather than local disk exhaustion or
+ a DiskSage lock. The UI keeps the explicit bounded Finder-cancel action as the only operator
+ mutation, while new copy, attestation, and source eviction remain fail-closed. No Finder/provider
+ process, CloudDocs database, source file, or cloud object was changed.
diff --git a/src-tauri/src/cloud.rs b/src-tauri/src/cloud.rs
index 8ef9556bb..88b21774a 100644
--- a/src-tauri/src/cloud.rs
+++ b/src-tauri/src/cloud.rs
@@ -5256,9 +5256,12 @@ pub fn plan_cloud_archive_from_snapshot(
}
#[cfg(not(coverage))]
let exact_duplicates = {
+ // Detect clusters before applying the presentation limit so a duplicate pair split across
+ // the boundary still blocks automatic handling of the visible member.
+ let exact_duplicates = mark_exact_duplicate_candidates(&mut candidates, None);
candidates.sort_by(|a, b| b.bytes.cmp(&a.bytes).then_with(|| a.src.cmp(&b.src)));
candidates.truncate(options.limit);
- mark_exact_duplicate_candidates(&mut candidates, None)
+ exact_duplicates
};
#[cfg(coverage)]
let exact_duplicates = {
@@ -5280,13 +5283,32 @@ pub fn plan_cloud_archive_from_snapshot(
"cloud-sync-unverified".into(),
"full-transfer-content-hash-pending".into(),
];
- if local_volume.as_ref().is_some_and(|volume| {
- candidates.iter().any(|candidate| {
- !crate::volume_pressure::has_copy_headroom(volume.available_bytes, candidate.bytes)
- })
- }) {
+ let mut destination_headroom_insufficient = false;
+ let mut destination_headroom_unverified = false;
+ for candidate in candidates
+ .iter()
+ .filter(|candidate| candidate.blocked_reason.is_none())
+ {
+ match crate::copy_headroom::require_destination_copy_headroom(
+ Path::new(&candidate.dst),
+ candidate.bytes,
+ now_ms,
+ ) {
+ Ok(()) => {}
+ Err(reason) if reason == "local-volume-headroom-insufficient" => {
+ destination_headroom_insufficient = true;
+ }
+ Err(_) => {
+ destination_headroom_unverified = true;
+ }
+ }
+ }
+ if destination_headroom_insufficient {
notices.push("local-volume-headroom-insufficient".into());
}
+ if destination_headroom_unverified {
+ notices.push("local-volume-headroom-unverified".into());
+ }
if !snapshot.source_scan_complete {
notices.push("source-scan-incomplete".into());
notices.push(format!(
@@ -7071,6 +7093,65 @@ mod tests {
.contains(&"exact-duplicate-content-needs-canonical-selection".to_string()));
}
+ #[cfg(not(coverage))]
+ #[test]
+ fn planner_detects_duplicate_pairs_split_by_candidate_limit() {
+ let tmp = tempfile::tempdir().unwrap();
+ let source = tmp.path().join("source");
+ let cloud = tmp.path().join("cloud");
+ writable_dir(&source);
+ writable_dir(&cloud);
+ std::fs::write(source.join("large.pdf"), b"larger-than-duplicates").unwrap();
+ for name in ["a-duplicate.pdf", "z-duplicate.pdf"] {
+ std::fs::write(source.join(name), b"same-content").unwrap();
+ }
+ let production_time_ms = date_epoch_ms(2026, 1, 2).unwrap();
+ let metadata = ContentMetadata {
+ production_time_ms: Some(production_time_ms),
+ production_time_source: Some("embedded:test:creation-date".into()),
+ production_time_confidence: Some("high".into()),
+ ..ContentMetadata::default()
+ };
+ let files = ["large.pdf", "a-duplicate.pdf", "z-duplicate.pdf"]
+ .into_iter()
+ .map(|name| {
+ let path = source.join(name);
+ let file_metadata = std::fs::metadata(&path).unwrap();
+ FileFact {
+ path,
+ bytes: file_metadata.len(),
+ created_ms: millis(file_metadata.created()),
+ modified_ms: millis(file_metadata.modified()),
+ content_metadata: metadata.clone(),
+ }
+ })
+ .collect::>();
+
+ let report = plan_cloud_archive(
+ &files,
+ &source,
+ &root(CloudProvider::GoogleDrive, &cloud),
+ system_now_ms() + DAY_MS,
+ CloudPlanOptions {
+ min_size_bytes: 0,
+ min_age_days: 0,
+ limit: 2,
+ },
+ );
+
+ assert_eq!(report.candidates.len(), 2);
+ assert_eq!(report.exact_duplicates.cluster_count, 1);
+ assert_eq!(report.exact_duplicates.candidate_count, 2);
+ let visible_duplicate = report
+ .candidates
+ .iter()
+ .find(|candidate| candidate.relative_path == "a-duplicate.pdf")
+ .unwrap();
+ assert!(visible_duplicate
+ .review_reasons
+ .contains(&"exact-duplicate-content-needs-canonical-selection".to_string()));
+ }
+
#[cfg(not(coverage))]
#[test]
fn canonical_recommendation_keeps_embedded_lineage_ahead_of_copy_name_heuristics() {
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 3f2f9bffd..a499356d8 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -30,9 +30,6 @@ use crate::{
#[path = "home_resolution.rs"]
mod home_resolution;
-#[path = "copy_headroom.rs"]
-mod copy_headroom;
-
#[derive(Default)]
pub struct AppState {
pub result: Arc>>,
@@ -1241,7 +1238,7 @@ pub fn inspect_icloud_new_copy_admission(
) -> Result {
let home = resolve_home(&app)?;
let mut report = icloud_sync_health::inspect_new_copy_admission(&home, cloud::system_now_ms())?;
- if !persist_icloud_health_evidence(&app, &report) {
+ if !persist_icloud_health_evidence(&app, &mut report) {
report
.notices
.push("icloud-sync-health-evidence-persistence-failed".into());
@@ -1261,7 +1258,13 @@ pub fn inspect_cloud_provider_global_sync(
if selected.provider == cloud::CloudProvider::Icloud {
return Err("provider-global-sync-icloud-specialized".into());
}
- provider_global_sync::inspect_new_copy_admission(selected.provider)
+ let mut report = provider_global_sync::inspect_new_copy_admission(selected.provider)?;
+ if !persist_provider_global_sync_evidence(&app, &mut report) {
+ report
+ .notices
+ .push("provider-global-sync-evidence-persistence-failed".into());
+ }
+ Ok(report)
}
#[cfg(not(coverage))]
@@ -1275,15 +1278,33 @@ struct CloudPlanningOutput {
#[cfg(not(coverage))]
fn persist_icloud_health_evidence(
app: &AppHandle,
- report: &icloud_sync_health::IcloudSyncHealthReport,
+ report: &mut icloud_sync_health::IcloudSyncHealthReport,
) -> bool {
- app.path()
- .app_data_dir()
- .ok()
- .and_then(|app_data_dir| {
- icloud_sync_health::write_icloud_sync_health_evidence(&app_data_dir, report).ok()
- })
- .is_some()
+ let Some(app_data_dir) = app.path().app_data_dir().ok() else {
+ return false;
+ };
+ if icloud_sync_health::write_icloud_sync_health_evidence(&app_data_dir, report).is_err() {
+ return false;
+ }
+ report.admission_blocked_since_ms =
+ icloud_sync_health::admission_blocked_since_ms(&app_data_dir, report);
+ true
+}
+
+#[cfg(not(coverage))]
+fn persist_provider_global_sync_evidence(
+ app: &AppHandle,
+ report: &mut provider_global_sync::ProviderGlobalSyncReport,
+) -> bool {
+ let Some(app_data_dir) = app.path().app_data_dir().ok() else {
+ return false;
+ };
+ if provider_global_sync::write_provider_global_sync_evidence(&app_data_dir, report).is_err() {
+ return false;
+ }
+ report.admission_blocked_since_ms =
+ provider_global_sync::provider_global_sync_blocked_since_ms(&app_data_dir, report);
+ true
}
#[cfg(not(coverage))]
@@ -1439,8 +1460,9 @@ fn cloud_plan_for_inputs(
}
let (icloud_health, provider_global_sync) = if selected.provider == cloud::CloudProvider::Icloud
{
- let health = icloud_sync_health::inspect_new_copy_admission(&home, cloud::system_now_ms()).ok();
- if let Some(health) = health.as_ref() {
+ let mut health =
+ icloud_sync_health::inspect_new_copy_admission(&home, cloud::system_now_ms()).ok();
+ if let Some(health) = health.as_mut() {
if !persist_icloud_health_evidence(app, health) {
report
.notices
@@ -1450,7 +1472,14 @@ fn cloud_plan_for_inputs(
icloud_sync_health::attach_new_copy_admission_notice(&mut report.notices, health.as_ref());
(health, None)
} else {
- let global_sync = provider_global_sync::inspect_new_copy_admission(selected.provider).ok();
+ let mut global_sync = provider_global_sync::inspect_new_copy_admission(selected.provider).ok();
+ if let Some(global_sync) = global_sync.as_mut() {
+ if !persist_provider_global_sync_evidence(app, global_sync) {
+ report
+ .notices
+ .push("provider-global-sync-evidence-persistence-failed".into());
+ }
+ }
provider_global_sync::attach_new_copy_admission_notice(
&mut report.notices,
global_sync.as_ref(),
@@ -1567,7 +1596,7 @@ fn require_capacity_for_copy(
#[cfg(not(coverage))]
fn require_local_copy_headroom(candidate: &cloud::CloudCandidate) -> Result<(), String> {
- copy_headroom::require_destination_copy_headroom(
+ crate::copy_headroom::require_destination_copy_headroom(
Path::new(&candidate.dst),
candidate.bytes,
cloud::system_now_ms(),
diff --git a/src-tauri/src/icloud_sync_health.rs b/src-tauri/src/icloud_sync_health.rs
index fb718eaab..7e0ad185a 100644
--- a/src-tauri/src/icloud_sync_health.rs
+++ b/src-tauri/src/icloud_sync_health.rs
@@ -43,8 +43,11 @@ const FILEPROVIDERCTL_PATH: &str = "/usr/bin/fileproviderctl";
const FILEPROVIDER_DUMP_TIMEOUT: Duration = Duration::from_secs(30);
#[cfg(target_os = "macos")]
// Keep the sync summary and a larger bounded provider-error window together; iCloud places
-// filename/root exclusion diagnostics after the aggregate summary in large dumps.
-const MAX_FILEPROVIDER_DUMP_BYTES: usize = 1024 * 1024;
+// filename/root exclusion diagnostics after the aggregate summary in large dumps. Match the
+// sibling provider_global_sync probe's cap: real fileproviderctl dumps observed in the field
+// run several MiB, and the previous 1 MiB cap routinely truncated before reaching per-item
+// exclusion/materialization markers that follow the aggregate summary.
+const MAX_FILEPROVIDER_DUMP_BYTES: usize = 32 * 1024 * 1024;
const ITEM_ERROR_AGE_NOTICE_MS: u64 = 86_400_000;
static SNAPSHOT_NONCE: AtomicU64 = AtomicU64::new(0);
@@ -53,6 +56,7 @@ pub const ICLOUD_NATIVE_STATUS_SCHEMA_VERSION: u32 = 1;
pub const ICLOUD_FILE_PROVIDER_ACTIVITY_SCHEMA_VERSION: u32 = 3;
pub const ICLOUD_SYNC_HEALTH_EVIDENCE_SCHEMA_VERSION: u32 = 1;
pub const ICLOUD_SYNC_HEALTH_EVIDENCE_DIRECTORY: &str = "icloud-sync-health-evidence";
+const FILE_PROVIDER_DISK_IMPORT_NOTICE: &str = "icloud-file-provider-disk-import-active";
const MAX_PERSISTED_HEALTH_SNAPSHOTS: usize = 128;
const MAX_PERSISTED_HEALTH_SNAPSHOT_BYTES: usize = 64 * 1024;
@@ -155,6 +159,9 @@ pub struct IcloudFileProviderActivityEvidence {
/// Aggregate provider errors where iCloud excludes an item under a sync root.
#[serde(default)]
pub sync_excluded_root_count: u64,
+ /// Aggregate File Provider metadata work still waiting to be indexed.
+ #[serde(default)]
+ pub pending_indexable_count: Option,
#[serde(default)]
pub active_upload_count: u64,
#[serde(default)]
@@ -246,6 +253,12 @@ pub struct IcloudSyncHealthReport {
pub schema_version: u32,
pub output_mode: String,
pub observed_at_ms: u64,
+ /// Earliest retained observation in the current admission-blocker run.
+ ///
+ /// This is derived from the bounded local evidence journal after the current observation is
+ /// persisted. It is diagnostic only and never authorizes a copy, attestation, or eviction.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub admission_blocked_since_ms: Option,
pub provider: String,
pub evidence_kind: String,
pub evidence_complete: bool,
@@ -569,6 +582,62 @@ fn prune_health_evidence(directory: &Path) -> Result<(), String> {
Ok(())
}
+#[cfg(not(coverage))]
+fn admission_blocker_key(blockers: &[String]) -> Vec {
+ let mut key = blockers.to_vec();
+ key.sort_unstable();
+ key.dedup();
+ key
+}
+
+/// Find the earliest retained observation with the same admission blockers.
+///
+/// The journal is bounded and each record is integrity-checked before it can extend the duration.
+/// An invalid or unreadable historical record stops the walk rather than manufacturing a longer
+/// stall interval from incomplete evidence.
+#[cfg(not(coverage))]
+pub fn admission_blocked_since_ms(
+ app_data_dir: &Path,
+ report: &IcloudSyncHealthReport,
+) -> Option {
+ let current_key = admission_blocker_key(&report.new_copy_admission_blockers);
+ if current_key.is_empty() || report.observed_at_ms == 0 {
+ return None;
+ }
+ let directory = health_evidence_directory(app_data_dir).ok()?;
+ let mut records = std::fs::read_dir(directory)
+ .ok()?
+ .filter_map(Result::ok)
+ .filter_map(|entry| {
+ let name = entry.file_name().into_string().ok()?;
+ is_health_evidence_record_name(&name).then_some((name, entry.path()))
+ })
+ .collect::>();
+ records.sort_by(|left, right| right.0.cmp(&left.0));
+
+ let mut since = report.observed_at_ms;
+ for (_, path) in records {
+ let encoded = match std::fs::read(path) {
+ Ok(encoded) => encoded,
+ Err(_) => break,
+ };
+ let snapshot = match serde_json::from_slice::(&encoded) {
+ Ok(snapshot) if validate_icloud_sync_health_evidence_snapshot(&snapshot).is_ok() => {
+ snapshot
+ }
+ _ => break,
+ };
+ if snapshot.observed_at_ms >= report.observed_at_ms {
+ continue;
+ }
+ if admission_blocker_key(&snapshot.new_copy_admission_blockers) != current_key {
+ break;
+ }
+ since = snapshot.observed_at_ms;
+ }
+ Some(since)
+}
+
fn system_time_ms(time: SystemTime) -> Option {
time.duration_since(UNIX_EPOCH)
.ok()
@@ -966,6 +1035,16 @@ fn parse_file_provider_activity_output(
.contains("excluded from sync under root")
})
.count() as u64;
+ let pending_indexable_count = output.lines().find_map(|line| {
+ let marker = line.trim().strip_prefix("+ ").unwrap_or(line.trim());
+ marker
+ .strip_prefix("pending-indexable-count:")
+ .and_then(|value| value.trim().parse::().ok())
+ });
+ let disk_import_active = output.lines().any(|line| {
+ let marker = line.trim().strip_prefix("+ ").unwrap_or(line.trim());
+ marker.eq_ignore_ascii_case("disk import: yes")
+ });
let active_upload_count = output
.lines()
.filter(|line| line.to_ascii_lowercase().contains("upload progress:"))
@@ -1005,6 +1084,12 @@ fn parse_file_provider_activity_output(
if sync_excluded_root_count > 0 {
notices.push("icloud-file-provider-sync-root-excluded-observed".into());
}
+ if pending_indexable_count.is_some_and(|count| count > 0) {
+ notices.push("icloud-file-provider-indexing-pending".into());
+ }
+ if disk_import_active {
+ notices.push(FILE_PROVIDER_DISK_IMPORT_NOTICE.into());
+ }
if active_upload_count > 0 {
notices.push("icloud-file-provider-active-upload".into());
}
@@ -1023,6 +1108,7 @@ fn parse_file_provider_activity_output(
staged_item_missing_count,
sync_excluded_filename_count,
sync_excluded_root_count,
+ pending_indexable_count,
active_upload_count,
active_download_count,
active_upload_progress_millionths,
@@ -1794,6 +1880,7 @@ fn build_report(
schema_version: ICLOUD_SYNC_HEALTH_SCHEMA_VERSION,
output_mode: "icloud-local-sync-health".into(),
observed_at_ms,
+ admission_blocked_since_ms: None,
provider: "icloud".into(),
evidence_kind: "supplementary-local-cloud-docs-private-schema".into(),
evidence_complete,
@@ -1917,6 +2004,16 @@ fn attach_native_status_admission(report: &mut IcloudSyncHealthReport) {
if activity.sync_excluded_root_count > 0 {
add_blocker("icloud-file-provider-root-excluded");
}
+ if activity.pending_indexable_count.is_some_and(|count| count > 0) {
+ add_blocker("icloud-file-provider-indexing-pending");
+ }
+ if activity
+ .notices
+ .iter()
+ .any(|notice| notice == FILE_PROVIDER_DISK_IMPORT_NOTICE)
+ {
+ add_blocker(FILE_PROVIDER_DISK_IMPORT_NOTICE);
+ }
if !no_progress && !materialization_failed {
if activity.active_upload_count > 0 || activity.active_download_count > 0 {
add_blocker("icloud-file-provider-transfer-active");
@@ -2258,6 +2355,38 @@ mod tests {
assert!(validate_file_provider_activity_evidence(&evidence).is_ok());
}
+ #[test]
+ fn file_provider_parser_records_pending_indexable_count() {
+ let evidence = parse_file_provider_activity_output(
+ "pending-indexable-count: 12474\n",
+ 42,
+ true,
+ false,
+ false,
+ );
+ assert_eq!(evidence.pending_indexable_count, Some(12_474));
+ assert!(evidence
+ .notices
+ .contains(&"icloud-file-provider-indexing-pending".to_string()));
+ assert!(validate_file_provider_activity_evidence(&evidence).is_ok());
+ }
+
+ #[test]
+ fn file_provider_parser_records_disk_import_without_paths() {
+ let evidence = parse_file_provider_activity_output(
+ "sync engine state:\n+ disk import: yes\n",
+ 42,
+ true,
+ false,
+ false,
+ );
+ assert!(evidence
+ .notices
+ .contains(&"icloud-file-provider-disk-import-active".to_string()));
+ assert!(validate_file_provider_activity_evidence(&evidence).is_ok());
+ assert!(!serde_json::to_string(&evidence).unwrap().contains("sync engine state"));
+ }
+
#[test]
fn file_provider_parser_records_materialization_failures_without_paths() {
let evidence = parse_file_provider_activity_output(
@@ -2631,6 +2760,46 @@ mod tests {
assert!(!first.exists());
}
+ #[cfg(not(coverage))]
+ #[test]
+ fn admission_blocked_since_uses_only_contiguous_matching_evidence() {
+ let directory = tempfile::tempdir().unwrap();
+ for observed_at_ms in 1..=2 {
+ let report = build_report(
+ observed_at_ms,
+ vec![],
+ parse_queue_rows(queue_output()).unwrap(),
+ false,
+ false,
+ )
+ .unwrap();
+ write_icloud_sync_health_evidence(directory.path(), &report).unwrap();
+ }
+
+ let current = build_report(
+ 3,
+ vec![],
+ parse_queue_rows(queue_output()).unwrap(),
+ false,
+ false,
+ )
+ .unwrap();
+ assert_eq!(
+ admission_blocked_since_ms(directory.path(), ¤t),
+ Some(1)
+ );
+
+ let mut changed = current.clone();
+ changed.observed_at_ms = 4;
+ changed
+ .new_copy_admission_blockers
+ .push("icloud-upload-out-of-quota".into());
+ assert_eq!(
+ admission_blocked_since_ms(directory.path(), &changed),
+ Some(4)
+ );
+ }
+
#[test]
fn health_evidence_rejects_unsafe_report_claims() {
let mut report =
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 608d55cab..5fab673f4 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -6,6 +6,8 @@ compile_error!("DiskSage supports only Windows, Linux, and macOS targets.");
mod dupes;
#[cfg_attr(coverage, allow(dead_code))]
mod commands;
+#[path = "copy_headroom.rs"]
+pub(crate) mod copy_headroom;
#[cfg_attr(coverage, allow(dead_code))]
mod node_navigation;
#[cfg_attr(coverage, allow(dead_code))]
diff --git a/src-tauri/src/naruon_cloud_copy_readiness.rs b/src-tauri/src/naruon_cloud_copy_readiness.rs
index 1ef38cf21..7e39258c7 100644
--- a/src-tauri/src/naruon_cloud_copy_readiness.rs
+++ b/src-tauri/src/naruon_cloud_copy_readiness.rs
@@ -31,7 +31,7 @@ const RUNTIME_BLOCKERS: [&str; 2] = [
"provider-client-runtime-not-observed",
"provider-client-runtime-evidence-unavailable",
];
-const ICLOUD_ADMISSION_BLOCKERS: [&str; 20] = [
+const ICLOUD_ADMISSION_BLOCKERS: [&str; 22] = [
"icloud-sync-health-evidence-incomplete",
"icloud-upload-queue-nonempty",
"icloud-upload-in-flight",
@@ -47,6 +47,8 @@ const ICLOUD_ADMISSION_BLOCKERS: [&str; 20] = [
"icloud-file-provider-materialization-failed",
"icloud-file-provider-filename-excluded",
"icloud-file-provider-root-excluded",
+ "icloud-file-provider-indexing-pending",
+ "icloud-file-provider-disk-import-active",
"icloud-file-provider-transfer-active",
"icloud-file-provider-dump-timeout",
"icloud-file-provider-dump-output-truncated",
@@ -333,6 +335,16 @@ fn expected_icloud_admission_blockers(report: &IcloudSyncHealthReport) -> Vec 0 {
blockers.push("icloud-file-provider-root-excluded".into());
}
+ if activity.pending_indexable_count.is_some_and(|count| count > 0) {
+ blockers.push("icloud-file-provider-indexing-pending".into());
+ }
+ if activity
+ .notices
+ .iter()
+ .any(|notice| notice == "icloud-file-provider-disk-import-active")
+ {
+ blockers.push("icloud-file-provider-disk-import-active".into());
+ }
if !no_progress && !materialization_failed
&& (activity.active_upload_count > 0 || activity.active_download_count > 0)
{
@@ -1191,6 +1203,16 @@ fn validate_icloud_admission_summary(
if activity.sync_excluded_root_count > 0 {
expected.push("icloud-file-provider-root-excluded".to_string());
}
+ if activity.pending_indexable_count.is_some_and(|count| count > 0) {
+ expected.push("icloud-file-provider-indexing-pending".to_string());
+ }
+ if activity
+ .notices
+ .iter()
+ .any(|notice| notice == "icloud-file-provider-disk-import-active")
+ {
+ expected.push("icloud-file-provider-disk-import-active".to_string());
+ }
if !no_progress && !materialization_failed
&& (activity.active_upload_count > 0 || activity.active_download_count > 0)
{
@@ -1408,6 +1430,7 @@ mod tests {
schema_version: ICLOUD_SYNC_HEALTH_SCHEMA_VERSION,
output_mode: "icloud-local-sync-health".into(),
observed_at_ms: 30,
+ admission_blocked_since_ms: None,
provider: "icloud".into(),
evidence_kind: "supplementary-local-cloud-docs-private-schema".into(),
evidence_complete: true,
@@ -1559,6 +1582,8 @@ mod tests {
schema_version: provider_global_sync::PROVIDER_GLOBAL_SYNC_SCHEMA_VERSION,
provider: CloudProvider::Onedrive,
evidence_kind: "fileproviderctl-global-dump".into(),
+ observed_at_ms: 1,
+ admission_blocked_since_ms: None,
evidence_complete: true,
state: ProviderGlobalSyncState::Pending,
upload_progress_present: true,
@@ -1924,7 +1949,7 @@ mod tests {
let mut forged_icloud_blocker =
export_naruon_cloud_copy_readiness(&onedrive_report, &runtime, None).unwrap();
forged_icloud_blocker.candidate_blocker_counts.insert(
- "icloud-upload-queue-nonempty".into(),
+ "icloud-file-provider-indexing-pending".into(),
CountBytes {
count: forged_icloud_blocker.candidate_count,
bytes: forged_icloud_blocker.candidate_bytes,
diff --git a/src-tauri/src/provider_evidence.rs b/src-tauri/src/provider_evidence.rs
index 705772121..f355365b7 100644
--- a/src-tauri/src/provider_evidence.rs
+++ b/src-tauri/src/provider_evidence.rs
@@ -216,9 +216,9 @@ fn prune_receipt_evidence_history(
}
records.sort_by(|left, right| (left.0, left.1.as_str()).cmp(&(right.0, right.1.as_str())));
let prune_count = records.len() - MAX_PROVIDER_EVIDENCE_RECORDS_PER_RECEIPT;
- for (_, _, path) in records
+ for (_, _record_id, path) in records
.into_iter()
- .filter(|(_, record_id, _)| record_id.as_str() != protected_record_id)
+ .filter(|(_, record_id, _)| record_id != protected_record_id)
.take(prune_count)
{
remove_retained_evidence_file(&path)?;
@@ -233,9 +233,8 @@ fn prune_receipt_evidence_history(
/// Persist the full provider claim before it is used to authorize source eviction.
///
/// The file is create-only, read-only, fsynced, and named by the receipt, observation time, and
-/// integrity digest. Existing evidence is never overwritten. Repeated attestations retain a
-/// bounded per-receipt history while preserving the just-written immutable record even if the
-/// local clock moves backwards.
+/// integrity digest. Existing evidence is never overwritten. Repeated attestations retain the
+/// newest bounded per-receipt history so background reconciliation cannot grow storage forever.
#[cfg(not(coverage))]
pub fn write_immutable_sync_evidence(
directory: &Path,
diff --git a/src-tauri/src/provider_global_sync.rs b/src-tauri/src/provider_global_sync.rs
index a4f4e45ce..5c5398793 100644
--- a/src-tauri/src/provider_global_sync.rs
+++ b/src-tauri/src/provider_global_sync.rs
@@ -7,6 +7,8 @@
use crate::cloud::CloudProvider;
use serde::{Deserialize, Serialize};
+use sha2::{Digest, Sha256};
+use std::path::{Path, PathBuf};
// macOS provider dumps include bounded item summaries even with --limit-dump-size. Keep enough
// room for real OneDrive/Google Drive dumps while retaining a hard memory ceiling.
@@ -41,6 +43,11 @@ pub struct ProviderGlobalSyncReport {
pub schema_version: u32,
pub provider: CloudProvider,
pub evidence_kind: String,
+ #[serde(default)]
+ pub observed_at_ms: u64,
+ /// Earliest retained observation in the current provider admission-blocker run.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub admission_blocked_since_ms: Option,
pub evidence_complete: bool,
pub state: ProviderGlobalSyncState,
pub upload_progress_present: bool,
@@ -167,14 +174,15 @@ pub fn parse_dump(
has_item_not_found |= marker_lower.contains("code=-1005")
|| marker_lower.contains("itemnotfound")
|| marker.contains("파일이 존재하지 않습니다");
- has_local_disk_full |= contains_bounded_numeric_marker(&marker_lower, "odresult_errno ", "28")
- || contains_bounded_numeric_marker(&marker_lower, "errno ", "28")
- || marker_lower.contains("enospc")
- || contains_bounded_numeric_marker(&marker_lower, "code=", "28")
- || contains_bounded_numeric_marker(&marker_lower, "code ", "28")
- || contains_bounded_numeric_marker(&marker_lower, "osstatus ", "-34")
- || marker_lower.contains("no space left on device")
- || marker_lower.contains("disk full");
+ has_local_disk_full |=
+ contains_bounded_numeric_marker(&marker_lower, "odresult_errno ", "28")
+ || contains_bounded_numeric_marker(&marker_lower, "errno ", "28")
+ || marker_lower.contains("enospc")
+ || contains_bounded_numeric_marker(&marker_lower, "code=", "28")
+ || contains_bounded_numeric_marker(&marker_lower, "code ", "28")
+ || contains_bounded_numeric_marker(&marker_lower, "osstatus -", "34")
+ || marker_lower.contains("no space left on device")
+ || marker_lower.contains("disk full");
if has_filename_too_long
|| has_temporarily_disconnected
|| has_server_unreachable
@@ -252,6 +260,8 @@ pub fn parse_dump(
schema_version: PROVIDER_GLOBAL_SYNC_SCHEMA_VERSION,
provider,
evidence_kind: "fileproviderctl-global-dump".into(),
+ observed_at_ms: 0,
+ admission_blocked_since_ms: None,
evidence_complete: !probe_timed_out,
state,
upload_progress_present,
@@ -382,7 +392,9 @@ pub fn inspect_new_copy_admission(
provider: CloudProvider,
) -> Result {
let output = run_dump(provider)?;
- parse_dump(provider, &output)
+ let mut report = parse_dump(provider, &output)?;
+ report.observed_at_ms = system_time_ms();
+ Ok(report)
}
#[cfg(not(target_os = "macos"))]
@@ -401,6 +413,287 @@ fn report_identity_is_valid(report: &ProviderGlobalSyncReport) -> bool {
&& provider_identifier(report.provider).is_some()
}
+pub const PROVIDER_GLOBAL_SYNC_EVIDENCE_SCHEMA_VERSION: u32 = 1;
+pub const PROVIDER_GLOBAL_SYNC_EVIDENCE_DIRECTORY: &str = "provider-global-sync-evidence";
+const MAX_PERSISTED_PROVIDER_GLOBAL_SYNC_SNAPSHOTS: usize = 128;
+const MAX_PERSISTED_PROVIDER_GLOBAL_SYNC_SNAPSHOT_BYTES: usize = 64 * 1024;
+
+/// Path-free provider-global evidence retained only to measure a blocker across restarts.
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
+pub struct ProviderGlobalSyncEvidenceSnapshot {
+ pub schema_version: u32,
+ pub observed_at_ms: u64,
+ pub provider: CloudProvider,
+ pub evidence_complete: bool,
+ pub state: ProviderGlobalSyncState,
+ pub upload_progress_present: bool,
+ pub download_progress_present: bool,
+ pub pending_indexable_count: Option,
+ pub blockers: Vec,
+ pub evidence_fingerprint_sha256: String,
+}
+
+fn system_time_ms() -> u64 {
+ use std::time::{SystemTime, UNIX_EPOCH};
+ SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .ok()
+ .and_then(|duration| u64::try_from(duration.as_millis()).ok())
+ .unwrap_or(0)
+}
+
+fn provider_global_sync_blocker_key(report: &ProviderGlobalSyncReport) -> String {
+ let mut blockers = report.blockers.clone();
+ blockers.sort_unstable();
+ blockers.dedup();
+ format!(
+ "{}|{}|{}|{}|{}|{}",
+ report.provider.as_str(),
+ report.state.as_str(),
+ report.upload_progress_present,
+ report.download_progress_present,
+ report
+ .pending_indexable_count
+ .is_some_and(|count| count > 0),
+ blockers.join(",")
+ )
+}
+
+fn provider_global_sync_snapshot_key(snapshot: &ProviderGlobalSyncEvidenceSnapshot) -> String {
+ let mut blockers = snapshot.blockers.clone();
+ blockers.sort_unstable();
+ blockers.dedup();
+ format!(
+ "{}|{}|{}|{}|{}|{}",
+ snapshot.provider.as_str(),
+ snapshot.state.as_str(),
+ snapshot.upload_progress_present,
+ snapshot.download_progress_present,
+ snapshot
+ .pending_indexable_count
+ .is_some_and(|count| count > 0),
+ blockers.join(",")
+ )
+}
+
+fn provider_global_sync_fingerprint(
+ snapshot: &ProviderGlobalSyncEvidenceSnapshot,
+) -> Result {
+ let mut unsigned = snapshot.clone();
+ unsigned.evidence_fingerprint_sha256.clear();
+ let encoded = serde_json::to_vec(&unsigned)
+ .map_err(|_| "provider-global-sync-evidence-fingerprint-encode-failed".to_string())?;
+ let digest = Sha256::digest(encoded);
+ Ok(digest.iter().map(|byte| format!("{byte:02x}")).collect())
+}
+
+pub fn provider_global_sync_evidence_snapshot_from_report(
+ report: &ProviderGlobalSyncReport,
+) -> Result {
+ if !report_identity_is_valid(report)
+ || report.observed_at_ms == 0
+ || report
+ .blockers
+ .iter()
+ .any(|blocker| !is_stable_provider_blocker(blocker))
+ {
+ return Err("provider-global-sync-evidence-claim-invalid".into());
+ }
+ let mut snapshot = ProviderGlobalSyncEvidenceSnapshot {
+ schema_version: PROVIDER_GLOBAL_SYNC_EVIDENCE_SCHEMA_VERSION,
+ observed_at_ms: report.observed_at_ms,
+ provider: report.provider,
+ evidence_complete: report.evidence_complete,
+ state: report.state,
+ upload_progress_present: report.upload_progress_present,
+ download_progress_present: report.download_progress_present,
+ pending_indexable_count: report.pending_indexable_count,
+ blockers: report.blockers.clone(),
+ evidence_fingerprint_sha256: String::new(),
+ };
+ snapshot.evidence_fingerprint_sha256 = provider_global_sync_fingerprint(&snapshot)?;
+ Ok(snapshot)
+}
+
+pub fn validate_provider_global_sync_evidence_snapshot(
+ snapshot: &ProviderGlobalSyncEvidenceSnapshot,
+) -> Result<(), String> {
+ if snapshot.schema_version != PROVIDER_GLOBAL_SYNC_EVIDENCE_SCHEMA_VERSION
+ || snapshot.observed_at_ms == 0
+ || provider_identifier(snapshot.provider).is_none()
+ || snapshot
+ .blockers
+ .iter()
+ .any(|blocker| !is_stable_provider_blocker(blocker))
+ {
+ return Err("provider-global-sync-evidence-shape-invalid".into());
+ }
+ let expected = provider_global_sync_fingerprint(snapshot)?;
+ if snapshot.evidence_fingerprint_sha256 != expected {
+ return Err("provider-global-sync-evidence-fingerprint-invalid".into());
+ }
+ Ok(())
+}
+
+#[cfg(not(coverage))]
+fn provider_global_sync_evidence_directory(app_data_dir: &Path) -> Result {
+ if !app_data_dir.is_absolute()
+ || app_data_dir
+ .components()
+ .any(|component| matches!(component, std::path::Component::ParentDir))
+ {
+ return Err("provider-global-sync-evidence-parent-invalid".into());
+ }
+ std::fs::create_dir_all(app_data_dir)
+ .map_err(|_| "provider-global-sync-evidence-parent-create-failed".to_string())?;
+ let parent = std::fs::symlink_metadata(app_data_dir)
+ .map_err(|_| "provider-global-sync-evidence-parent-unavailable".to_string())?;
+ if parent.file_type().is_symlink() || !parent.is_dir() {
+ return Err("provider-global-sync-evidence-parent-unsafe".into());
+ }
+ let directory = app_data_dir.join(PROVIDER_GLOBAL_SYNC_EVIDENCE_DIRECTORY);
+ std::fs::create_dir_all(&directory)
+ .map_err(|_| "provider-global-sync-evidence-directory-create-failed".to_string())?;
+ let metadata = std::fs::symlink_metadata(&directory)
+ .map_err(|_| "provider-global-sync-evidence-directory-unavailable".to_string())?;
+ if metadata.file_type().is_symlink() || !metadata.is_dir() {
+ return Err("provider-global-sync-evidence-directory-unsafe".into());
+ }
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ std::fs::set_permissions(&directory, std::fs::Permissions::from_mode(0o700)).map_err(
+ |_| "provider-global-sync-evidence-directory-permissions-failed".to_string(),
+ )?;
+ }
+ Ok(directory)
+}
+
+#[cfg(not(coverage))]
+fn prune_provider_global_sync_evidence(directory: &Path) -> Result<(), String> {
+ let mut records = std::fs::read_dir(directory)
+ .map_err(|_| "provider-global-sync-evidence-directory-read-failed".to_string())?
+ .filter_map(Result::ok)
+ .filter_map(|entry| {
+ let name = entry.file_name().into_string().ok()?;
+ let (timestamp, fingerprint) = name.strip_suffix(".json")?.split_once('-')?;
+ (timestamp.len() == 20
+ && timestamp.bytes().all(|byte| byte.is_ascii_digit())
+ && fingerprint.len() == 64
+ && fingerprint
+ .bytes()
+ .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)))
+ .then_some((name, entry.path()))
+ })
+ .collect::>();
+ records.sort_by(|left, right| left.0.cmp(&right.0));
+ while records.len() > MAX_PERSISTED_PROVIDER_GLOBAL_SYNC_SNAPSHOTS {
+ let (_, path) = records.remove(0);
+ std::fs::remove_file(path)
+ .map_err(|_| "provider-global-sync-evidence-retention-failed".to_string())?;
+ }
+ Ok(())
+}
+
+/// Persist a bounded, path-free provider observation; it never mutates the cloud root.
+#[cfg(not(coverage))]
+pub fn write_provider_global_sync_evidence(
+ app_data_dir: &Path,
+ report: &ProviderGlobalSyncReport,
+) -> Result {
+ use std::io::Write;
+ let snapshot = provider_global_sync_evidence_snapshot_from_report(report)?;
+ validate_provider_global_sync_evidence_snapshot(&snapshot)?;
+ let directory = provider_global_sync_evidence_directory(app_data_dir)?;
+ let path = directory.join(format!(
+ "{:020}-{}.json",
+ snapshot.observed_at_ms, snapshot.evidence_fingerprint_sha256
+ ));
+ let encoded = serde_json::to_vec_pretty(&snapshot)
+ .map_err(|_| "provider-global-sync-evidence-encode-failed".to_string())?;
+ if encoded.len() > MAX_PERSISTED_PROVIDER_GLOBAL_SYNC_SNAPSHOT_BYTES {
+ return Err("provider-global-sync-evidence-too-large".into());
+ }
+ let mut options = std::fs::OpenOptions::new();
+ options.write(true).create_new(true);
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::OpenOptionsExt;
+ options.mode(0o400);
+ }
+ let mut file = options
+ .open(&path)
+ .map_err(|_| "provider-global-sync-evidence-create-failed".to_string())?;
+ let result = file
+ .write_all(&encoded)
+ .and_then(|_| file.sync_all())
+ .map_err(|_| "provider-global-sync-evidence-write-failed".to_string());
+ if let Err(error) = result {
+ drop(file);
+ let _ = std::fs::remove_file(&path);
+ return Err(error);
+ }
+ #[cfg(unix)]
+ std::fs::File::open(&directory)
+ .and_then(|directory| directory.sync_all())
+ .map_err(|_| "provider-global-sync-evidence-directory-sync-failed".to_string())?;
+ prune_provider_global_sync_evidence(&directory)?;
+ Ok(path)
+}
+
+/// Return the earliest retained observation with the same provider blocker fingerprint.
+#[cfg(not(coverage))]
+pub fn provider_global_sync_blocked_since_ms(
+ app_data_dir: &Path,
+ report: &ProviderGlobalSyncReport,
+) -> Option {
+ if report.blockers.is_empty() || report.observed_at_ms == 0 {
+ return None;
+ }
+ let current_key = provider_global_sync_blocker_key(report);
+ let directory = provider_global_sync_evidence_directory(app_data_dir).ok()?;
+ let mut records = std::fs::read_dir(directory)
+ .ok()?
+ .filter_map(Result::ok)
+ .filter_map(|entry| {
+ let name = entry.file_name().into_string().ok()?;
+ name.strip_suffix(".json")?.split_once('-')?;
+ Some((name, entry.path()))
+ })
+ .collect::>();
+ records.sort_by(|left, right| right.0.cmp(&left.0));
+ let mut since = report.observed_at_ms;
+ for (_, path) in records {
+ let encoded = match std::fs::read(path) {
+ Ok(encoded) => encoded,
+ Err(_) => break,
+ };
+ let snapshot = match serde_json::from_slice::(&encoded)
+ {
+ Ok(snapshot) => snapshot,
+ Err(_) => break,
+ };
+ if validate_provider_global_sync_evidence_snapshot(&snapshot).is_err() {
+ break;
+ }
+ if snapshot.provider != report.provider {
+ continue;
+ }
+ if !snapshot.evidence_complete {
+ break;
+ }
+ if snapshot.observed_at_ms >= report.observed_at_ms {
+ continue;
+ }
+ if provider_global_sync_snapshot_key(&snapshot) != current_key {
+ break;
+ }
+ since = snapshot.observed_at_ms;
+ }
+ Some(since)
+}
+
fn report_has_pending_aggregate_evidence(report: &ProviderGlobalSyncReport) -> bool {
report.upload_progress_present
|| report.download_progress_present
@@ -723,4 +1016,101 @@ sync engine state:
assert!(parse_dump(CloudProvider::Onedrive, "sync engine state:").is_err());
assert!(parse_dump(CloudProvider::Icloud, QUIET_DUMP).is_err());
}
+
+ #[test]
+ fn provider_blocker_onset_survives_restart_without_retaining_paths() {
+ let directory = tempfile::tempdir().unwrap();
+ let mut first = parse_dump(CloudProvider::GoogleDrive, ACTIVE_DUMP).unwrap();
+ first.observed_at_ms = 1_000;
+ write_provider_global_sync_evidence(directory.path(), &first).unwrap();
+
+ let mut second = first.clone();
+ second.observed_at_ms = 2_000;
+ write_provider_global_sync_evidence(directory.path(), &second).unwrap();
+
+ assert_eq!(
+ provider_global_sync_blocked_since_ms(directory.path(), &second),
+ Some(1_000)
+ );
+ let encoded = std::fs::read_dir(
+ directory
+ .path()
+ .join(PROVIDER_GLOBAL_SYNC_EVIDENCE_DIRECTORY),
+ )
+ .unwrap()
+ .next()
+ .unwrap()
+ .unwrap();
+ let contents = std::fs::read_to_string(encoded.path()).unwrap();
+ assert!(!contents.contains("/Users/"));
+ assert!(!contents.contains("fileproviderctl"));
+ }
+
+ #[test]
+ fn provider_blocker_onset_ignores_interleaved_provider_evidence() {
+ let directory = tempfile::tempdir().unwrap();
+ let mut google = parse_dump(CloudProvider::GoogleDrive, ACTIVE_DUMP).unwrap();
+ google.observed_at_ms = 1_000;
+ write_provider_global_sync_evidence(directory.path(), &google).unwrap();
+
+ let onedrive_dump = ACTIVE_DUMP.replace(
+ "com.google.drivefs.fpext",
+ "com.microsoft.OneDrive.FileProvider",
+ );
+ let mut onedrive = parse_dump(CloudProvider::Onedrive, &onedrive_dump).unwrap();
+ onedrive.observed_at_ms = 1_500;
+ write_provider_global_sync_evidence(directory.path(), &onedrive).unwrap();
+
+ let mut later_google = google.clone();
+ later_google.observed_at_ms = 2_000;
+ assert_eq!(
+ provider_global_sync_blocked_since_ms(directory.path(), &later_google),
+ Some(1_000)
+ );
+ }
+
+ #[test]
+ fn malformed_older_provider_evidence_preserves_newer_onset() {
+ let directory = tempfile::tempdir().unwrap();
+ let mut report = parse_dump(CloudProvider::GoogleDrive, ACTIVE_DUMP).unwrap();
+ report.observed_at_ms = 1_500;
+ write_provider_global_sync_evidence(directory.path(), &report).unwrap();
+ let malformed_path = directory
+ .path()
+ .join(PROVIDER_GLOBAL_SYNC_EVIDENCE_DIRECTORY)
+ .join(format!("{:020}-malformed.json", 1_000));
+ std::fs::write(malformed_path, b"not-json").unwrap();
+
+ let later = ProviderGlobalSyncReport {
+ observed_at_ms: 2_000,
+ ..report
+ };
+ assert_eq!(
+ provider_global_sync_blocked_since_ms(directory.path(), &later),
+ Some(1_500)
+ );
+ }
+
+ #[test]
+ fn tampered_provider_evidence_cannot_extend_blocker_duration() {
+ let directory = tempfile::tempdir().unwrap();
+ let mut report = parse_dump(CloudProvider::GoogleDrive, ACTIVE_DUMP).unwrap();
+ report.observed_at_ms = 1_000;
+ write_provider_global_sync_evidence(directory.path(), &report).unwrap();
+ let mut snapshot = provider_global_sync_evidence_snapshot_from_report(&report).unwrap();
+ snapshot.observed_at_ms = 1;
+ let tampered_path = directory
+ .path()
+ .join(PROVIDER_GLOBAL_SYNC_EVIDENCE_DIRECTORY)
+ .join(format!("{:020}-{}.json", 1, "0".repeat(64)));
+ std::fs::write(tampered_path, serde_json::to_vec(&snapshot).unwrap()).unwrap();
+ let later = ProviderGlobalSyncReport {
+ observed_at_ms: 2_000,
+ ..report
+ };
+ assert_eq!(
+ provider_global_sync_blocked_since_ms(directory.path(), &later),
+ Some(1_000)
+ );
+ }
}
diff --git a/src-tauri/src/provider_sync.rs b/src-tauri/src/provider_sync.rs
index 83c183e5e..637500fd6 100644
--- a/src-tauri/src/provider_sync.rs
+++ b/src-tauri/src/provider_sync.rs
@@ -1232,6 +1232,21 @@ mod tests {
);
}
+ #[test]
+ fn local_current_but_not_uploaded_is_pending_upload_evidence() {
+ let output = uploaded_file_provider_output().replace("isUploaded = 1", "isUploaded = 0");
+ let snapshot = parse_file_providerctl_snapshot(&output, 42, "content-hash").unwrap();
+ assert!(snapshot.is_local_current());
+ assert!(!snapshot.is_sync_complete());
+
+ let evidence =
+ evidence_from_file_provider_snapshot(&receipt(CloudProvider::Onedrive), &snapshot, 30)
+ .unwrap();
+ assert!(!evidence.sync_complete);
+ assert_eq!(evidence.sync_state, ProviderSyncState::PendingUpload);
+ assert_eq!(incomplete_sync_blocker(evidence.sync_complete), Some("provider-sync-incomplete"));
+ }
+
#[test]
fn trashed_file_provider_item_remains_incomplete() {
let output = uploaded_file_provider_output().replace("isTrashed = 0", "isTrashed = 1");
diff --git a/src-tauri/tests/cloud_copy_headroom_destination_contract.rs b/src-tauri/tests/cloud_copy_headroom_destination_contract.rs
index 5049170ed..434e1ee96 100644
--- a/src-tauri/tests/cloud_copy_headroom_destination_contract.rs
+++ b/src-tauri/tests/cloud_copy_headroom_destination_contract.rs
@@ -24,3 +24,20 @@ fn native_copy_headroom_is_bound_to_the_destination_staging_volume() {
"source-volume free space must not authorize or veto destination staging"
);
}
+
+#[test]
+fn cloud_plan_preview_headroom_uses_destination_staging_volume() {
+ let cloud = include_str!("../src/cloud.rs");
+ let start = cloud
+ .find("let mut destination_headroom_insufficient")
+ .expect("cloud preview must evaluate destination headroom");
+ let tail = &cloud[start..];
+ let end = tail
+ .find("\n if !snapshot.source_scan_complete")
+ .expect("destination preview gate must remain before source-scan notices");
+ let helper = &tail[..end];
+
+ assert!(helper.contains("require_destination_copy_headroom"));
+ assert!(helper.contains("candidate.dst"));
+ assert!(!helper.contains("candidate.src"));
+}
diff --git a/src-tauri/tests/naruon_active_fileprovider_transfer.rs b/src-tauri/tests/naruon_active_fileprovider_transfer.rs
index f7c1d4244..ed3b6f504 100644
--- a/src-tauri/tests/naruon_active_fileprovider_transfer.rs
+++ b/src-tauri/tests/naruon_active_fileprovider_transfer.rs
@@ -50,10 +50,12 @@ fn icloud_report() -> CloudPlanReport {
fn active_transfer_health() -> IcloudSyncHealthReport {
let blocker = "icloud-file-provider-transfer-active".to_string();
+ let disk_import_blocker = "icloud-file-provider-disk-import-active".to_string();
IcloudSyncHealthReport {
schema_version: ICLOUD_SYNC_HEALTH_SCHEMA_VERSION,
output_mode: "icloud-local-sync-health".into(),
observed_at_ms: 30,
+ admission_blocked_since_ms: None,
provider: "icloud".into(),
evidence_kind: "supplementary-local-cloud-docs-private-schema".into(),
evidence_complete: true,
@@ -81,16 +83,20 @@ fn active_transfer_health() -> IcloudSyncHealthReport {
staged_item_missing_count: 0,
sync_excluded_filename_count: 0,
sync_excluded_root_count: 0,
+ pending_indexable_count: None,
active_upload_count: 1,
active_download_count: 0,
active_upload_progress_millionths: Some(500_000),
active_download_progress_millionths: None,
- notices: vec!["icloud-file-provider-dump-read-only".into()],
+ notices: vec![
+ "icloud-file-provider-dump-read-only".into(),
+ disk_import_blocker.clone(),
+ ],
}),
sync_backlog_present: true,
new_copy_admission_state: "blocked".into(),
- new_copy_admission_blockers: vec![blocker.clone()],
- blockers: vec![blocker],
+ new_copy_admission_blockers: vec![disk_import_blocker.clone(), blocker.clone()],
+ blockers: vec![disk_import_blocker, blocker],
notices: Vec::new(),
paths_redacted: true,
user_filenames_read: false,
@@ -121,5 +127,9 @@ fn active_fileprovider_transfer_exports_blocked_readiness() {
.blockers
.iter()
.any(|blocker| blocker == "icloud-file-provider-transfer-active"));
+ assert!(admission
+ .blockers
+ .iter()
+ .any(|blocker| blocker == "icloud-file-provider-disk-import-active"));
assert!(validate_naruon_cloud_copy_readiness(&envelope).is_ok());
}
diff --git a/src-tauri/tests/naruon_readiness_global_sync_identity.rs b/src-tauri/tests/naruon_readiness_global_sync_identity.rs
index 4840b0a76..790edd94c 100644
--- a/src-tauri/tests/naruon_readiness_global_sync_identity.rs
+++ b/src-tauri/tests/naruon_readiness_global_sync_identity.rs
@@ -56,6 +56,8 @@ fn canonical_clear_report() -> ProviderGlobalSyncReport {
schema_version: PROVIDER_GLOBAL_SYNC_SCHEMA_VERSION,
provider: CloudProvider::Onedrive,
evidence_kind: "fileproviderctl-global-dump".into(),
+ observed_at_ms: 1,
+ admission_blocked_since_ms: None,
evidence_complete: true,
state: ProviderGlobalSyncState::Clear,
upload_progress_present: false,
diff --git a/src-tauri/tests/provider_global_sync_clear_state_integrity.rs b/src-tauri/tests/provider_global_sync_clear_state_integrity.rs
index ce8a80bd9..277a49bb9 100644
--- a/src-tauri/tests/provider_global_sync_clear_state_integrity.rs
+++ b/src-tauri/tests/provider_global_sync_clear_state_integrity.rs
@@ -15,6 +15,8 @@ fn clear_report() -> ProviderGlobalSyncReport {
schema_version: PROVIDER_GLOBAL_SYNC_SCHEMA_VERSION,
provider: CloudProvider::Onedrive,
evidence_kind: "fileproviderctl-global-dump".into(),
+ observed_at_ms: 1,
+ admission_blocked_since_ms: None,
evidence_complete: true,
state: ProviderGlobalSyncState::Clear,
upload_progress_present: false,
diff --git a/src-tauri/tests/provider_global_sync_disk_full_code_boundary.rs b/src-tauri/tests/provider_global_sync_disk_full_code_boundary.rs
index 61284d34b..c6a9c3a71 100644
--- a/src-tauri/tests/provider_global_sync_disk_full_code_boundary.rs
+++ b/src-tauri/tests/provider_global_sync_disk_full_code_boundary.rs
@@ -8,20 +8,31 @@ use disksage_lib::provider_global_sync::{parse_dump, ProviderGlobalSyncState};
#[test]
fn code_28_marker_requires_a_numeric_boundary() {
- let unrelated = "com.google.drivefs.fpext\nsync engine state:\n error:'NSFileProviderErrorDomain Code=280 unrelated provider failure'\n";
- let unrelated_report = parse_dump(CloudProvider::GoogleDrive, unrelated).unwrap();
- assert_eq!(unrelated_report.state, ProviderGlobalSyncState::Error);
- assert!(unrelated_report
- .blockers
- .contains(&"provider-global-sync-error".into()));
- assert!(!unrelated_report
- .blockers
- .contains(&"provider-global-sync-local-disk-full".into()));
+ for marker in [
+ "NSFileProviderErrorDomain Code=280 unrelated provider failure",
+ "write failed: errno 280",
+ "write failed: odresult_errno 280",
+ "write failed: OSStatus -340",
+ ] {
+ let unrelated =
+ format!("com.google.drivefs.fpext\nsync engine state:\n error:'{marker}'\n");
+ let unrelated_report = parse_dump(CloudProvider::GoogleDrive, &unrelated).unwrap();
+ assert_eq!(unrelated_report.state, ProviderGlobalSyncState::Error);
+ assert!(unrelated_report
+ .blockers
+ .contains(&"provider-global-sync-error".into()));
+ assert!(!unrelated_report
+ .blockers
+ .contains(&"provider-global-sync-local-disk-full".into()));
+ }
for marker in [
"NSFileProviderErrorDomain Code=28 write failed",
"NSFileProviderErrorDomain Code 28 write failed",
"NSFileProviderErrorDomain Code=28",
+ "write failed: errno 28",
+ "write failed: odresult_errno 28",
+ "write failed: OSStatus -34",
] {
let dump = format!("com.google.drivefs.fpext\nsync engine state:\n {marker}\n");
let report = parse_dump(CloudProvider::GoogleDrive, &dump).unwrap();
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index 1acf18312..5a56eac32 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -15,6 +15,10 @@
type CloudReviewQueueSort,
} from "./cloudReviewQueue";
import { boundedCloudArchiveErrorMessage } from "./cloudArchiveErrorFeedback";
+ import {
+ blockedSinceMs as resolveBlockedSinceMs,
+ icloudBlockedSinceMs as resolveIcloudBlockedSinceMs,
+ } from "./cloudArchiveHealthTiming";
import { fmtBytes } from "./fmt";
import IcloudLocalEviction from "./IcloudLocalEviction.svelte";
@@ -229,15 +233,16 @@
&& (!candidate.requires_review || exactApproval)
&& (embeddedHighConfidence || exactApproval)
&& capacityEvidenceAvailable
- && api.localCopyHasHeadroom(report?.local_volume, candidate.bytes)
+ && !nativeCopyHeadroomBlocked(candidate)
&& !providerAdmissionBlocked
&& !icloudAdmissionBlocked
&& !icloudPreCopyEvidenceBlocked
&& approvalPhrase !== null;
}
- function nativeCopyHeadroomBlocked(candidate: api.CloudCandidate): boolean {
- return !api.localCopyHasHeadroom(report?.local_volume, candidate.bytes);
+ function nativeCopyHeadroomBlocked(_candidate: api.CloudCandidate): boolean {
+ return report?.notices.includes("local-volume-headroom-insufficient") === true
+ || report?.notices.includes("local-volume-headroom-unverified") === true;
}
function providerApiWriteConnected(): boolean {
@@ -500,6 +505,7 @@
activity?.no_progress_create_count ?? 0,
activity?.materialization_failure_count ?? 0,
activity?.staged_item_missing_count ?? 0,
+ activity?.pending_indexable_count ?? "",
activity?.active_upload_count ?? 0,
activity?.active_download_count ?? 0,
activity?.active_upload_progress_millionths ?? "",
@@ -512,7 +518,10 @@
icloudHealthBlockedSinceMs = 0;
icloudHealthFingerprint = "";
} else if (icloudHealthFingerprint !== fingerprint) {
- icloudHealthBlockedSinceMs = observedAtMs;
+ icloudHealthBlockedSinceMs = resolveIcloudBlockedSinceMs(
+ next.admission_blocked_since_ms,
+ next.observed_at_ms,
+ );
icloudHealthFingerprint = fingerprint;
}
icloudHealth = next;
@@ -570,6 +579,13 @@
try {
const observedAtMs = Date.now();
const next = await api.inspectCloudProviderGlobalSync(root.path);
+ const backendObservedAtMs = Number.isInteger(next.observed_at_ms) && next.observed_at_ms > 0
+ ? next.observed_at_ms
+ : observedAtMs;
+ const backendBlockedSinceMs = resolveBlockedSinceMs(
+ next.admission_blocked_since_ms,
+ backendObservedAtMs,
+ );
const fingerprint = [
next.provider,
next.state,
@@ -582,12 +598,14 @@
providerGlobalSyncBlockedSinceMs = 0;
providerGlobalSyncFingerprint = "";
} else if (providerGlobalSyncFingerprint !== fingerprint) {
- providerGlobalSyncBlockedSinceMs = observedAtMs;
+ providerGlobalSyncBlockedSinceMs = backendBlockedSinceMs;
providerGlobalSyncFingerprint = fingerprint;
+ } else if (backendBlockedSinceMs < providerGlobalSyncBlockedSinceMs) {
+ providerGlobalSyncBlockedSinceMs = backendBlockedSinceMs;
}
providerGlobalSync = next;
- providerGlobalSyncObservedAtMs = observedAtMs;
- providerGlobalSyncNextCheckAt = observedAtMs
+ providerGlobalSyncObservedAtMs = backendObservedAtMs;
+ providerGlobalSyncNextCheckAt = backendObservedAtMs
+ (next.blockers.length === 0
? RECONCILIATION_INTERVAL_MS
: PROVIDER_GLOBAL_SYNC_BLOCKED_RETRY_INTERVAL_MS);
@@ -796,6 +814,8 @@
"icloud-file-provider-materialization-failed": "File Provider 파일 materialization이 실패함(staged item 없음)",
"icloud-file-provider-filename-excluded": "iCloud가 파일 이름 때문에 동기화에서 제외한 항목이 있음",
"icloud-file-provider-root-excluded": "iCloud가 동기화 루트에서 제외한 항목이 있음",
+ "icloud-file-provider-indexing-pending": "iCloud File Provider 메타데이터 색인 대기 항목이 있음",
+ "icloud-file-provider-disk-import-active": "macOS File Provider 디스크 가져오기가 진행 중임",
"icloud-file-provider-transfer-active": "File Provider 기존 upload/download가 진행 중임",
"icloud-file-provider-dump-timeout": "File Provider 상태 확인이 시간 초과됨",
"icloud-file-provider-dump-output-truncated": "File Provider 상태 증거가 잘려 불완전함",
@@ -921,6 +941,8 @@
{#if icloudHealth.file_provider_activity}
· File Provider 무진행 fetch {icloudHealth.file_provider_activity.no_progress_fetch_count}개 / create {icloudHealth.file_provider_activity.no_progress_create_count}개 ·
materialization 실패 {icloudHealth.file_provider_activity.materialization_failure_count}개 / staged item 없음 {icloudHealth.file_provider_activity.staged_item_missing_count}개 ·
+ 색인 대기 {icloudHealth.file_provider_activity.pending_indexable_count ?? 0}개 ·
+ 디스크 import {icloudHealth.file_provider_activity.notices.includes("icloud-file-provider-disk-import-active") ? "진행 중" : "없음"} ·
활성 upload {icloudHealth.file_provider_activity.active_upload_count}개 / download {icloudHealth.file_provider_activity.active_download_count}개
{/if}
@@ -946,6 +968,8 @@
|| icloudHealth.file_provider_activity.no_progress_create_count > 0
|| icloudHealth.file_provider_activity.materialization_failure_count > 0
|| icloudHealth.file_provider_activity.staged_item_missing_count > 0
+ || (icloudHealth.file_provider_activity.pending_indexable_count ?? 0) > 0
+ || icloudHealth.file_provider_activity.notices.includes("icloud-file-provider-disk-import-active")
|| icloudHealth.file_provider_activity.timed_out
|| icloudHealth.file_provider_activity.active_upload_count > 0
|| icloudHealth.file_provider_activity.active_download_count > 0
@@ -979,6 +1003,18 @@
clear가 될 때까지 Finder 복사와 원본 정리를 진행하지 않습니다.
+ iCloud File Provider에 메타데이터 색인 대기 항목이 {icloudHealth.file_provider_activity?.pending_indexable_count}개 있습니다.
+ Finder의 “복사 준비 중” 단계가 이 대기열을 기다릴 수 있으므로, 색인 대기와 기존 전송이 해소되기 전에는 복사를 완료로 간주하지 않습니다.
+
동일한 iCloud 차단 상태가 15분 이상 지속되었습니다. Finder에 남은 복사 대기를 취소하고,
@@ -1200,8 +1236,8 @@
{/if}
{#if report.candidates.some(nativeCopyHeadroomBlocked)}
- 네이티브 File Provider 복사는 후보 크기와 {fmtBytes(api.LOCAL_COPY_RESERVE_BYTES)} 여유공간을 함께 확보해야 합니다.
- 현재 여유공간이 부족한 후보는 버튼을 비활성화합니다. 명시적 OAuth 공급자 API 업로드는 별도 경로입니다.
+ 네이티브 File Provider 복사는 목적지 staging 볼륨에 후보 크기와 {fmtBytes(api.LOCAL_COPY_RESERVE_BYTES)} 여유공간을 함께 확보해야 합니다.
+ 목적지 여유공간이 부족하거나 확인되지 않은 경우 native 버튼을 비활성화합니다. 명시적 OAuth 공급자 API 업로드는 별도 경로입니다.
{/if}
{/if}
diff --git a/src/lib/api.ts b/src/lib/api.ts
index 97859a9b3..27a6e62e1 100644
--- a/src/lib/api.ts
+++ b/src/lib/api.ts
@@ -826,6 +826,8 @@ export interface LocalVolumeSnapshot {
export interface IcloudSyncHealthReport {
observed_at_ms: number;
+ /** Earliest retained observation for the current admission-blocker run. */
+ admission_blocked_since_ms?: number | null;
evidence_complete: boolean;
managed_database_allocated_bytes?: number;
upload_queue: {
@@ -845,6 +847,7 @@ export interface IcloudSyncHealthReport {
staged_item_missing_count: number;
sync_excluded_filename_count: number;
sync_excluded_root_count: number;
+ pending_indexable_count?: number | null;
active_upload_count: number;
active_download_count: number;
active_upload_progress_millionths?: number | null;
@@ -865,6 +868,8 @@ export interface ProviderGlobalSyncReport {
schema_version: number;
provider: Exclude;
evidence_kind: string;
+ observed_at_ms: number;
+ admission_blocked_since_ms?: number | null;
evidence_complete: boolean;
state: ProviderGlobalSyncState;
upload_progress_present: boolean;
diff --git a/src/lib/cloudArchiveAdmissionContract.test.ts b/src/lib/cloudArchiveAdmissionContract.test.ts
index 4fe28521e..93d2b2391 100644
--- a/src/lib/cloudArchiveAdmissionContract.test.ts
+++ b/src/lib/cloudArchiveAdmissionContract.test.ts
@@ -8,6 +8,7 @@ const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..")
describe("CloudArchive iCloud admission contract", () => {
it("clears stale health evidence when refresh fails", () => {
const source = readFileSync(resolve(repositoryRoot, "src/lib/CloudArchive.svelte"), "utf8");
+ const apiSource = readFileSync(resolve(repositoryRoot, "src/lib/api.ts"), "utf8");
expect(source).toContain("icloudHealth = null;");
expect(source).toContain("icloudHealth?.new_copy_admission_state !== \"clear\"");
expect(source).toContain("managed_database_allocated_bytes");
@@ -16,6 +17,10 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("동기화 진단:");
expect(source).toContain("iCloud File Provider 증거를 확인하지 못했습니다.");
expect(source).toContain("no_progress_create_count");
+ expect(source).toContain("pending_indexable_count");
+ expect(source).toContain("icloud-file-provider-indexing-pending");
+ expect(source).toContain("icloud-file-provider-disk-import-active");
+ expect(source).toContain("디스크 import");
expect(source).toContain("Finder에 남은 복사 대기는 취소");
expect(source).toContain("File Provider 상태 확인이 제한시간을 넘었습니다");
expect(source).toContain("Lineage 연결관계");
@@ -28,11 +33,21 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("icloudHealthBlockedSinceMs");
expect(source).toContain("icloudHealthFingerprint");
expect(source).toContain("const admissionClear = next.new_copy_admission_state === \"clear\"");
+ expect(source).toContain('from "./cloudArchiveHealthTiming"');
+ expect(source).toContain("resolveIcloudBlockedSinceMs(");
+ expect(source).toContain("next.admission_blocked_since_ms,");
+ expect(source).toContain("next.observed_at_ms,");
+ expect(source).not.toContain("next.admission_blocked_since_ms ?? observedAtMs");
+ expect(apiSource).toContain("admission_blocked_since_ms?: number | null;");
expect(source).toContain("동일한 iCloud 차단 상태가 15분 이상 지속되었습니다.");
expect(source).toContain("refreshIcloudHealth(true)");
expect(source).toContain("refreshProviderGlobalSync(true)");
expect(source).toContain("const observedAtMs = Date.now();");
expect(source).toContain("providerGlobalSyncBlockedSinceMs");
+ expect(source).toContain("const backendObservedAtMs = Number.isInteger(next.observed_at_ms)");
+ expect(source).toContain("const backendBlockedSinceMs = resolveBlockedSinceMs(");
+ expect(source).toContain("providerGlobalSyncObservedAtMs = backendObservedAtMs;");
+ expect(source).toContain("providerGlobalSyncBlockedSinceMs = backendBlockedSinceMs;");
expect(source).toContain("PROVIDER_GLOBAL_SYNC_BLOCKED_RETRY_INTERVAL_MS");
expect(source).toContain("providerGlobalSyncNextCheckAt");
expect(source).toContain("checkingProviderGlobalSync || (!force && Date.now() < providerGlobalSyncNextCheckAt)");
@@ -56,6 +71,10 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("provider-global-sync-item-not-found");
expect(source).toContain("cancellingFinderCopy || checkingProviderGlobalSync");
expect(source).toContain("finderCopyCancelStatus = \"Finder 복사 취소 요청을 보냈습니다. 상태를 다시 확인하십시오.\"");
+ expect(source).toContain("local-volume-headroom-insufficient");
+ expect(source).toContain("local-volume-headroom-unverified");
+ expect(source).toContain("!nativeCopyHeadroomBlocked(candidate)");
+ expect(source).not.toContain("api.localCopyHasHeadroom(report?.local_volume, candidate.bytes)");
});
it("does not run the heavy iCloud probe for non-iCloud selected roots", () => {
diff --git a/src/lib/cloudArchiveHealthTiming.test.ts b/src/lib/cloudArchiveHealthTiming.test.ts
new file mode 100644
index 000000000..a2dac25ad
--- /dev/null
+++ b/src/lib/cloudArchiveHealthTiming.test.ts
@@ -0,0 +1,24 @@
+import { describe, expect, it } from "vitest";
+import { blockedSinceMs, icloudBlockedSinceMs } from "./cloudArchiveHealthTiming";
+
+describe("iCloud health blocker timing", () => {
+ it("uses the backend observation clock when persisted blocked-since is absent", () => {
+ expect(icloudBlockedSinceMs(null, 20_000)).toBe(20_000);
+ expect(icloudBlockedSinceMs(undefined, 30_000)).toBe(30_000);
+ });
+
+ it("preserves the backend-provided blocker onset", () => {
+ expect(icloudBlockedSinceMs(10_000, 20_000)).toBe(10_000);
+ });
+
+ it("rejects impossible persisted onset values", () => {
+ for (const onset of [-1, 20_001, Number.NaN, Number.POSITIVE_INFINITY]) {
+ expect(icloudBlockedSinceMs(onset, 20_000)).toBe(20_000);
+ }
+ });
+
+ it("uses the same persisted timing contract for third-party providers", () => {
+ expect(blockedSinceMs(40_000, 50_000)).toBe(40_000);
+ expect(blockedSinceMs(null, 50_000)).toBe(50_000);
+ });
+});
diff --git a/src/lib/cloudArchiveHealthTiming.ts b/src/lib/cloudArchiveHealthTiming.ts
new file mode 100644
index 000000000..a7e09984f
--- /dev/null
+++ b/src/lib/cloudArchiveHealthTiming.ts
@@ -0,0 +1,19 @@
+export function blockedSinceMs(
+ admissionBlockedSinceMs: number | null | undefined,
+ backendObservedAtMs: number,
+): number {
+ const persistedOnsetIsUsable = typeof admissionBlockedSinceMs === "number"
+ && Number.isSafeInteger(admissionBlockedSinceMs)
+ && admissionBlockedSinceMs >= 0
+ && admissionBlockedSinceMs <= backendObservedAtMs;
+ return persistedOnsetIsUsable
+ ? admissionBlockedSinceMs
+ : backendObservedAtMs;
+}
+
+export function icloudBlockedSinceMs(
+ admissionBlockedSinceMs: number | null | undefined,
+ backendObservedAtMs: number,
+): number {
+ return blockedSinceMs(admissionBlockedSinceMs, backendObservedAtMs);
+}
diff --git a/src/lib/cloudOffloadGoalProjectionContract.test.ts b/src/lib/cloudOffloadGoalProjectionContract.test.ts
index f80324450..dfd1a3018 100644
--- a/src/lib/cloudOffloadGoalProjectionContract.test.ts
+++ b/src/lib/cloudOffloadGoalProjectionContract.test.ts
@@ -29,6 +29,7 @@ describe("cloud-offload Goal projection contract", () => {
expect(goal.pre_copy_evidence_streams).toEqual(expect.arrayContaining([
"provider-client-runtime-evidence",
"icloud-sync-health-evidence",
+ "provider-global-sync-evidence",
]));
expect(goal.lineage_relation_identifier_rule).toContain("never a raw local or provider path");
});
From c9ac3b2041cc6736fb60fde773c3c6fbd21fcdc2 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:25:31 +0900
Subject: [PATCH 02/86] docs: record latest iCloud indexing backlog
---
.../adr/0006-redacted-icloud-health-evidence.md | 15 +++++++++++++++
docs/product-technical-gap-baseline.md | 14 ++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 40d6f28bf..17fcf074a 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -115,3 +115,18 @@ attestation. The decision therefore remains unchanged: DiskSage reports the reco
backlog, offers only the explicit bounded Finder-cancel action, and keeps copy, attestation, and
source eviction fail-closed. No provider process, CloudDocs database, source, or cloud object was
mutated.
+
+## Operational evidence update — 2026-08-24 14:11
+
+The exact-head `disksage-icloud-sync-health` binary completed another read-only CloudDocs/WAL
+snapshot with `evidence_complete=true` and `new_copy_admission_state=blocked`. Aggregate upload
+backlog remained 343 items blocked on sync-up and one active upload remained at 95.24%; File
+Provider pending indexable items increased from 74,946 to 103,013 while one download and the
+disk-import/transfer notices remained active. The `real_datasets` target still had 14 entries,
+512 bytes, and mtime `2026-08-20 03:28:07 +0900`, with about 94 GiB available on `/`.
+
+The observation remains supplementary global provider evidence. It does not identify a Finder item
+or attest a cloud write, so `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+`mutation_performed=false` remain required. DiskSage continues to expose only the explicit bounded
+Finder-cancel action and never restarts provider processes or mutates provider, source, or cloud
+state from this evidence.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 01c840b6b..f8f6a7ab8 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1026,3 +1026,17 @@ independent approvals, last-push approval, resolved threads, and normal merge/sq
a DiskSage lock. The UI keeps the explicit bounded Finder-cancel action as the only operator
mutation, while new copy, attestation, and source eviction remain fail-closed. No Finder/provider
process, CloudDocs database, source file, or cloud object was changed.
+
+## 2026-08-24 14:11 +0900 live iCloud probe confirms worsening backlog
+
+- The exact-head `disksage-icloud-sync-health` binary completed another read-only CloudDocs/WAL
+ snapshot with `evidence_complete=true`, `new_copy_admission_state=blocked`,
+ `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+ `mutation_performed=false`.
+- The aggregate provider state still has 343 uploads blocked on sync-up and one active upload at
+ 95.24%; File Provider pending indexable items increased from 74,946 to 103,013, with one active
+ download and disk-import/transfer activity still present. This is provider reconciliation
+ evidence, not proof that any Finder item reached the cloud.
+- The target remained 14 entries, 512 bytes, and mtime `2026-08-20 03:28:07 +0900`; `/` retained
+ about 94 GiB available. DiskSage therefore continues to block new copy, attestation, and source
+ eviction. The only available operator mutation remains the explicit Finder-cancel action.
From 32c3df9bc7692ae9b70b24faf111972b9ea83e99 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:27:41 +0900
Subject: [PATCH 03/86] docs: refresh exact-head PR audit
---
docs/product-technical-gap-baseline.md | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index f8f6a7ab8..d6d08542f 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1040,3 +1040,23 @@ independent approvals, last-push approval, resolved threads, and normal merge/sq
- The target remained 14 entries, 512 bytes, and mtime `2026-08-20 03:28:07 +0900`; `/` retained
about 94 GiB available. DiskSage therefore continues to block new copy, attestation, and source
eviction. The only available operator mutation remains the explicit Finder-cancel action.
+
+## 2026-08-24 14:27 +0900 exact-head PR audit
+
+The following live heads were re-queried before this documentation update; predecessor reviews and
+checks are not reused:
+
+- DiskSage #189 is open/non-draft at `288904ff8b81d769847869f7b434065d7613b1d7` after absorbing
+ current `main`; required checks are queued/in progress, with no unresolved review threads.
+- DiskSage #212 is open/non-draft at `779afa48cc8bc534a6e5cc910714324d85f7358b`; its OAuth help
+ contract and dead-entrypoint fixes are pushed, required checks are queued/in progress, and all
+ current review threads are resolved.
+- DiskSage #238 is merged at `d44b23bdf4108bf6b6f6378f7e0ac305187deec6`; it is no longer an open
+ merge candidate.
+- DiskSage #247 is draft/open at `c9ac3b2041cc6736fb60fde773c3c6fbd21fcdc2`; the latest iCloud
+ evidence/ADR update is pushed, required checks are queued, and no review thread is unresolved.
+- DiskSage #249 remains draft/open at `44390608d30417477f6a66601b18a53ca87b0a9c`; its previous
+ Strix failure remains a provider-gate issue and is not treated as a product merge approval.
+- Central `.github` #1263 is open/non-draft at `14cd0e8438b6d670a0f036d1e47f35bd4c3f97a7`; the
+ cross-repository documentation reference is qualified, but protected checks/reviews are pending.
+ No merge is inferred from queued checks or bot comments.
From 16f511f8af8320ccd885c06c1de60ad00dfbbf12 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:32:32 +0900
Subject: [PATCH 04/86] docs: record current iCloud copy stall evidence
---
.../adr/0006-redacted-icloud-health-evidence.md | 16 ++++++++++++++++
docs/product-technical-gap-baseline.md | 15 +++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 17fcf074a..98b63112e 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -130,3 +130,19 @@ or attest a cloud write, so `provider_sync_attested=false`, `local_eviction_auth
`mutation_performed=false` remain required. DiskSage continues to expose only the explicit bounded
Finder-cancel action and never restarts provider processes or mutates provider, source, or cloud
state from this evidence.
+
+## Operational evidence update — 2026-08-24 14:31
+
+A fresh read-only health receipt observed `evidence_complete=true` and
+`new_copy_admission_state=blocked`. Aggregate upload state remained 343 items blocked on sync-up
+with one active upload at 95.24%; one active download and File Provider indexing, disk-import, and
+transfer activity remained, while pending indexable items increased to 110,652. Native status
+continued to report `client_state=needs-sync` with sync-up/down pending, and filename/root
+exclusions were still present.
+
+The root volume had about 83 GiB available and a bounded `lsof` sample found no handle on the
+`real_datasets` target while Finder remained at “preparing to copy”. This is provider
+reconciliation/indexing evidence, not disk exhaustion or per-item cloud-write proof. The decision
+is unchanged: keep `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+`mutation_performed=false`; expose only the explicit bounded Finder-cancel action and never
+restart providers or mutate provider, source, or cloud state from this aggregate receipt.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index d6d08542f..d96d57004 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1060,3 +1060,18 @@ checks are not reused:
- Central `.github` #1263 is open/non-draft at `14cd0e8438b6d670a0f036d1e47f35bd4c3f97a7`; the
cross-repository documentation reference is qualified, but protected checks/reviews are pending.
No merge is inferred from queued checks or bot comments.
+
+## 2026-08-24 14:31 +0900 live iCloud probe confirms copy-preparation stall
+
+- A fresh exact-head `disksage-icloud-sync-health` read-only probe again reported
+ `evidence_complete=true`, `new_copy_admission_state=blocked`, `provider_sync_attested=false`,
+ `local_eviction_authorized=false`, and `mutation_performed=false`.
+- The aggregate state remained 343 uploads blocked on sync-up with one active upload at 95.24%,
+ one active download, and File Provider pending indexable items increased to 110,652. Native
+ status still reported `client_state=needs-sync` with sync-up/down pending; filename/root
+ exclusions and disk-import/transfer activity remained present.
+- The root volume had about 83 GiB available (13% used), and a bounded `lsof` sample found no
+ handle on `real_datasets`. The Finder “preparing to copy” dialog is therefore a provider
+ reconciliation/indexing stall, not local disk exhaustion or evidence of a completed cloud
+ copy. DiskSage keeps copy, attestation, and source eviction fail-closed; only the explicit
+ bounded Finder-cancel action is available to the operator.
From 3557ef9a83cba96e82bbb65edf68c6ed470eb20c Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Sun, 23 Aug 2026 22:37:05 -0700
Subject: [PATCH 05/86] test: exercise destination headroom preview authority
---
...cloud_plan_destination_headroom_runtime.rs | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
new file mode 100644
index 000000000..6a3bd611a
--- /dev/null
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -0,0 +1,77 @@
+use disksage_lib::cloud::{
+ plan_cloud_archive, CloudAccountScope, CloudPlanOptions, CloudProvider, CloudRoot, ContentMetadata,
+ FileFact,
+};
+
+#[cfg(unix)]
+#[test]
+fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
+ use std::os::unix::fs::symlink;
+
+ let fixture = tempfile::tempdir().unwrap();
+ let source_root = fixture.path().join("source");
+ let cloud_root = fixture.path().join("cloud");
+ let redirected_archive = fixture.path().join("redirected-archive");
+ std::fs::create_dir(&source_root).unwrap();
+ std::fs::create_dir(&cloud_root).unwrap();
+ std::fs::create_dir(&redirected_archive).unwrap();
+
+ let source_file = source_root.join("report.pdf");
+ std::fs::write(&source_file, b"report").unwrap();
+
+ // The final candidate itself does not exist, so ordinary destination-exists checks do not
+ // block it. The nearest existing staging ancestor is nevertheless a symlink and must not
+ // become capacity authority for a native-copy preview.
+ symlink(
+ &redirected_archive,
+ cloud_root.join("DiskSage Archive"),
+ )
+ .unwrap();
+
+ let file = FileFact {
+ path: source_file,
+ bytes: 6,
+ created_ms: 1,
+ modified_ms: 1,
+ content_metadata: ContentMetadata::default(),
+ };
+ let root = CloudRoot {
+ id: "google-drive:test".into(),
+ provider: CloudProvider::GoogleDrive,
+ account_scope: CloudAccountScope::Personal,
+ label: "Google Drive".into(),
+ path: cloud_root.to_string_lossy().into_owned(),
+ readable: true,
+ access_issue: None,
+ };
+
+ let report = plan_cloud_archive(
+ &[file],
+ &source_root,
+ &root,
+ 86_400_001,
+ CloudPlanOptions {
+ min_size_bytes: 1,
+ min_age_days: 0,
+ limit: 10,
+ },
+ );
+
+ assert_eq!(report.candidates.len(), 1);
+ assert_eq!(report.candidates[0].blocked_reason, None);
+ assert!(
+ report
+ .notices
+ .iter()
+ .any(|notice| notice == "local-volume-headroom-unverified"),
+ "preview must reject an unsafe destination/staging capacity authority even when the source volume is healthy",
+ );
+ assert!(
+ report.local_volume.is_some(),
+ "source-volume pressure remains independent diagnostics rather than staging authority",
+ );
+ assert!(
+ !redirected_archive.join("documents").join("report.pdf").exists(),
+ "dry-run planning must not materialize the redirected destination",
+ );
+}
From 35ca9c39c2e86574d1500efc26a513c5cf12e22a Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:38:52 +0900
Subject: [PATCH 06/86] docs: refresh exact-head queue status
---
docs/product-technical-gap-baseline.md | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index d96d57004..4fc6bc039 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1075,3 +1075,16 @@ checks are not reused:
reconciliation/indexing stall, not local disk exhaustion or evidence of a completed cloud
copy. DiskSage keeps copy, attestation, and source eviction fail-closed; only the explicit
bounded Finder-cancel action is available to the operator.
+
+## 2026-08-24 14:38 +0900 exact-head queue refresh
+
+- DiskSage #189 advanced to `8809e6cdc8da14915a9e0219481f75a1faebfdb9` after absorbing current
+ `main`; it is open/non-draft with required checks pending and no unresolved review threads.
+- DiskSage #212 remains open/non-draft at `779afa48cc8bc534a6e5cc910714324d85f7358b`; checks are
+ pending and no qualifying approval is present.
+- DiskSage #247 is draft/open at `16f511f8af8320ccd885c06c1de60ad00dfbbf12`; the current iCloud
+ evidence update is pushed, checks are re-running, and no review thread is unresolved.
+- DiskSage #249 remains draft/open at `44390608d30417477f6a66601b18a53ca87b0a9c`; its prior
+ provider-gated Strix result is not merge evidence. Central `.github` #1263 has advanced to
+ `7011fee275eaa257ce491efb4812dd3e98ed649e` and remains blocked with changes requested.
+ No merge is inferred from queued checks or bot comments.
From 618acff21b78ba93a40a7c0d48b99961ba79f4dc Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Sun, 23 Aug 2026 22:40:12 -0700
Subject: [PATCH 07/86] test: preserve folded mail metadata regression across
restack
---
.../folded_received_header_plan_regression.rs | 63 +++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 src-tauri/tests/folded_received_header_plan_regression.rs
diff --git a/src-tauri/tests/folded_received_header_plan_regression.rs b/src-tauri/tests/folded_received_header_plan_regression.rs
new file mode 100644
index 000000000..3d516511c
--- /dev/null
+++ b/src-tauri/tests/folded_received_header_plan_regression.rs
@@ -0,0 +1,63 @@
+use disksage_lib::cloud::{
+ plan_cloud_archive, CloudAccountScope, CloudPlanOptions, CloudProvider, CloudRoot, ContentMetadata,
+ FileFact,
+};
+
+#[test]
+fn folded_received_header_at_end_of_block_is_safe_through_public_plan() {
+ let fixture = tempfile::tempdir().unwrap();
+ let source_root = fixture.path().join("source");
+ let cloud_root = fixture.path().join("cloud");
+ std::fs::create_dir(&source_root).unwrap();
+ std::fs::create_dir(&cloud_root).unwrap();
+
+ let message = concat!(
+ "Date: Mon, 17 Aug 2026 12:00:00 +0000\r\n",
+ "Subject: Folded Received regression\r\n",
+ "Received: from relay.example\r\n",
+ "\tby mx.example with ESMTP\r\n",
+ "\r\n",
+ "body is deliberately outside the bounded metadata parser\r\n",
+ );
+ let message_path = source_root.join("folded-received.eml");
+ std::fs::write(&message_path, message.as_bytes()).unwrap();
+
+ let report = plan_cloud_archive(
+ &[FileFact {
+ path: message_path,
+ bytes: message.len() as u64,
+ created_ms: 1,
+ modified_ms: 1,
+ content_metadata: ContentMetadata::default(),
+ }],
+ &source_root,
+ &CloudRoot {
+ id: "google-drive:test".into(),
+ provider: CloudProvider::GoogleDrive,
+ account_scope: CloudAccountScope::Personal,
+ label: "Google Drive".into(),
+ path: cloud_root.to_string_lossy().into_owned(),
+ readable: true,
+ access_issue: None,
+ },
+ 86_400_001,
+ CloudPlanOptions {
+ min_size_bytes: 1,
+ min_age_days: 0,
+ limit: 10,
+ },
+ );
+
+ assert_eq!(report.candidates.len(), 1);
+ let candidate = &report.candidates[0];
+ assert_eq!(candidate.content_title.as_deref(), Some("Folded Received regression"));
+ assert!(candidate.metadata_evidence.iter().any(|evidence| {
+ evidence.field == "email-header-bytes-inspected"
+ && evidence.source == "local:metadata-probe:bounded-rfc5322-header"
+ }));
+ assert!(candidate.metadata_evidence.iter().any(|evidence| {
+ evidence.field == "email-body-inspected"
+ && evidence.value == "false"
+ && evidence.source == "local:metadata-probe:bounded-rfc5322-header"
+ }));
+}
From a279484591528e5045faba4cc64df8530672bb09 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:40:52 +0900
Subject: [PATCH 08/86] docs: record latest exact-head queue
---
docs/product-technical-gap-baseline.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 4fc6bc039..0d3a7dbbe 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1088,3 +1088,14 @@ checks are not reused:
provider-gated Strix result is not merge evidence. Central `.github` #1263 has advanced to
`7011fee275eaa257ce491efb4812dd3e98ed649e` and remains blocked with changes requested.
No merge is inferred from queued checks or bot comments.
+
+## 2026-08-24 14:40 +0900 exact-head queue refresh
+
+- DiskSage #247 advanced to `618acff21b78ba93a40a7c0d48b99961ba79f4dc` with an additional public
+ plan regression for folded mail headers; the preceding destination-headroom test and iCloud
+ evidence remain in the exact ancestry. Checks are re-running and the draft remains blocked.
+- The other live references are unchanged: #189 `8809e6cdc8da14915a9e0219481f75a1faebfdb9`,
+ #212 `779afa48cc8bc534a6e5cc910714324d85f7358b`, #249
+ `44390608d30417477f6a66601b18a53ca87b0a9c`, and central `.github` #1263
+ `7011fee275eaa257ce491efb4812dd3e98ed649e`. No protected merge is inferred from pending
+ checks, historical reviews, or bot comments.
From beb81a857e6a80ae9dfbaf8bea7a5ef2367310b9 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:45:42 +0900
Subject: [PATCH 09/86] docs: refresh live product queue evidence
---
docs/product-technical-gap-baseline.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 0d3a7dbbe..c0dd3c902 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1099,3 +1099,15 @@ checks are not reused:
`44390608d30417477f6a66601b18a53ca87b0a9c`, and central `.github` #1263
`7011fee275eaa257ce491efb4812dd3e98ed649e`. No protected merge is inferred from pending
checks, historical reviews, or bot comments.
+
+## 2026-08-24 14:45 +0900 exact-head product queue refresh
+
+- DiskSage #247 is now exact head `f23539684651e9280962271759841f9d0fdd377a`, a draft/open
+ provider-indexing follow-up that also contains the folded-header and destination-headroom
+ regressions plus the standards-safe UI label convergence. Local frontend checks passed on this
+ tree; hosted checks are pending and no review thread is unresolved.
+- The next product gaps are visible in the live queue: #246 `9cf11c0194aece52a2769b9d10b8f20b7d2658e5`
+ (accessible Storybook UX contracts) and #244 `b9941295ac354bb63cf911a064a1f4df1f8eb60b`
+ (Rust 1.97.1 baseline) are draft/open; #189 remains `8809e6c`, and #212 remains `779afa4`.
+ Central `.github` #1263 remains `7011fee` with changes requested. Protected merge is not inferred
+ from draft status, queued checks, or historical approvals.
From e9a3fd8aefc013476bcd6c1ef990f8df19ed2bca Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:47:21 +0900
Subject: [PATCH 10/86] test: keep destination headroom fixture fresh
---
.../cloud_plan_destination_headroom_runtime.rs | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
index 6a3bd611a..fcc20baec 100644
--- a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -1,6 +1,6 @@
use disksage_lib::cloud::{
plan_cloud_archive, CloudAccountScope, CloudPlanOptions, CloudProvider, CloudRoot, ContentMetadata,
- FileFact,
+ FileFact, system_now_ms,
};
#[cfg(unix)]
@@ -18,6 +18,14 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
let source_file = source_root.join("report.pdf");
std::fs::write(&source_file, b"report").unwrap();
+ let source_metadata = std::fs::metadata(&source_file).unwrap();
+ let modified_ms = source_metadata
+ .modified()
+ .unwrap()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap()
+ .as_millis() as u64;
+ let observed_at_ms = system_now_ms();
// The final candidate itself does not exist, so ordinary destination-exists checks do not
// block it. The nearest existing staging ancestor is nevertheless a symlink and must not
@@ -30,9 +38,9 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
let file = FileFact {
path: source_file,
- bytes: 6,
- created_ms: 1,
- modified_ms: 1,
+ bytes: source_metadata.len(),
+ created_ms: observed_at_ms,
+ modified_ms,
content_metadata: ContentMetadata::default(),
};
let root = CloudRoot {
@@ -49,7 +57,7 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
&[file],
&source_root,
&root,
- 86_400_001,
+ observed_at_ms,
CloudPlanOptions {
min_size_bytes: 1,
min_age_days: 0,
From 43b696b2a14d54b9b6015d5ff3f40b3629ca80ec Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:47:56 +0900
Subject: [PATCH 11/86] docs: record destination fixture regression repair
---
docs/product-technical-gap-baseline.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index c0dd3c902..d0f2d05b5 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1111,3 +1111,14 @@ checks are not reused:
(Rust 1.97.1 baseline) are draft/open; #189 remains `8809e6c`, and #212 remains `779afa4`.
Central `.github` #1263 remains `7011fee` with changes requested. Protected merge is not inferred
from draft status, queued checks, or historical approvals.
+
+## 2026-08-24 14:47 +0900 exact-head regression repair
+
+- A local exact-head run initially exposed `source-snapshot-stale` in #247's destination-headroom
+ test because its fixture used sentinel timestamps (`created_ms=1`, `modified_ms=1`) for a file
+ that the public planner revalidates. The test—not the destination-authority implementation—was
+ stale. Head `e9a3fd8` now binds the fixture bytes/mtime to the materialized source and uses the
+ observed clock.
+- Pinned Rust 1.97.1 execution now passes both runtime regressions: destination ancestor headroom
+ authority and folded mail-header planning (2 passed). This preserves the real source freshness
+ gate while testing the intended symlinked-staging safety behavior.
From 92560015153d281fa5373ca096272ada4efe5b2f Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 14:56:47 +0900
Subject: [PATCH 12/86] docs: record latest iCloud stall evidence
---
.../adr/0006-redacted-icloud-health-evidence.md | 15 +++++++++++++++
docs/product-technical-gap-baseline.md | 10 ++++++++++
2 files changed, 25 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 98b63112e..7c572af40 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -146,3 +146,18 @@ reconciliation/indexing evidence, not disk exhaustion or per-item cloud-write pr
is unchanged: keep `provider_sync_attested=false`, `local_eviction_authorized=false`, and
`mutation_performed=false`; expose only the explicit bounded Finder-cancel action and never
restart providers or mutate provider, source, or cloud state from this aggregate receipt.
+
+## Operational evidence update — 2026-08-24 14:55
+
+The next bounded read-only CloudDocs/WAL snapshot completed with
+`evidence_complete=true` and `new_copy_admission_state=blocked`. The upload queue still contained
+343 items blocked on sync-up; one active upload remained at 95.24% and one active download was
+present. File Provider pending indexable items increased to 121,859, with the same disk-import,
+transfer, filename-exclusion, and root-exclusion notices. Native status continued to report
+`client_state=needs-sync` and sync-up/down pending.
+
+The root volume still had 66 GiB available, the 14-entry `real_datasets` directory remained 512
+bytes with its 2026-08-20 mtime, and the bounded `lsof` sample found no handle on that directory.
+This is a worsening provider reconciliation/indexing backlog, not local disk exhaustion or
+per-item cloud-write proof. The existing decision therefore remains fail-closed:
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index d0f2d05b5..988935155 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1122,3 +1122,13 @@ checks are not reused:
- Pinned Rust 1.97.1 execution now passes both runtime regressions: destination ancestor headroom
authority and folded mail-header planning (2 passed). This preserves the real source freshness
gate while testing the intended symlinked-staging safety behavior.
+
+## 2026-08-24 14:55 +0900 live iCloud recheck
+
+- The bounded `disksage-icloud-sync-health` probe completed with `evidence_complete=true` and
+ `new_copy_admission_state=blocked`: 343 uploads remain blocked on sync-up, one upload is active
+ at 95.24%, one download is active, and File Provider pending indexable items reached 121,859.
+- The root volume has 66 GiB available; `real_datasets` still has 14 entries and 512 bytes with
+ its 2026-08-20 mtime, and the bounded `lsof` sample has no handle on that directory. This
+ confirms provider reconciliation/indexing stall evidence rather than disk exhaustion or a
+ Finder copy receipt. Copy, per-item attestation, and source eviction remain fail-closed.
From 8b0be93c31a5c26a2bc169572e571829f679cfb5 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 15:35:13 +0900
Subject: [PATCH 13/86] docs: record latest icloud backlog evidence
---
.../adr/0006-redacted-icloud-health-evidence.md | 12 ++++++++++++
docs/product-technical-gap-baseline.md | 12 ++++++++++++
2 files changed, 24 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 7c572af40..7149b85f9 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -161,3 +161,15 @@ bytes with its 2026-08-20 mtime, and the bounded `lsof` sample found no handle o
This is a worsening provider reconciliation/indexing backlog, not local disk exhaustion or
per-item cloud-write proof. The existing decision therefore remains fail-closed:
`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
+
+## Operational evidence update — 2026-08-24 15:34
+
+The next bounded read-only receipt still reported `evidence_complete=true` and
+`new_copy_admission_state=blocked`. The 343-item sync-up backlog and one active upload at 95.24%
+were unchanged, while File Provider pending indexable items increased to 128,917; one download,
+disk import, transfer activity, and the 28 filename/2 root exclusions remained present. Native
+status continued to report `client_state=needs-sync` with `needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`.
+
+This increasing aggregate queue is stronger provider-stall evidence but still cannot identify the
+seven Finder items or prove a cloud write. The observation remains read-only and keeps
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 988935155..e3d7c6be3 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1132,3 +1132,15 @@ checks are not reused:
its 2026-08-20 mtime, and the bounded `lsof` sample has no handle on that directory. This
confirms provider reconciliation/indexing stall evidence rather than disk exhaustion or a
Finder copy receipt. Copy, per-item attestation, and source eviction remain fail-closed.
+
+## 2026-08-24 15:34 +0900 iCloud queue continues to grow
+
+- The next bounded read-only receipt still reported `evidence_complete=true` and
+ `new_copy_admission_state=blocked`: 343 uploads remained blocked on sync-up, one upload stayed
+ active at 95.24%, and one download stayed active. File Provider pending indexable items grew
+ from 121,859 to 128,917; disk import, transfer activity, and the 28 filename/2 root exclusions
+ remained present. Native status remained `client_state=needs-sync` with sync-up pending.
+- This is provider-global reconciliation evidence, not a per-item receipt for the seven
+ `real_datasets` entries and not proof of a cloud write. DiskSage keeps Goal
+ `provider-sync-incomplete`, copy/attestation/source eviction fail-closed, and performed no
+ Finder, provider, source, or cloud mutation.
From 336d83b0ca41b6373f546f9e40b8d31c5b2e7667 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 15:42:24 +0900
Subject: [PATCH 14/86] docs: separate strix provider evidence
---
docs/product-technical-gap-baseline.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index e3d7c6be3..1be5dc312 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1144,3 +1144,14 @@ checks are not reused:
`real_datasets` entries and not proof of a cloud write. DiskSage keeps Goal
`provider-sync-incomplete`, copy/attestation/source eviction fail-closed, and performed no
Finder, provider, source, or cloud mutation.
+
+## 2026-08-24 15:42 +0900 Strix provider evidence separated from source readiness
+
+- The exact-head central `.github` PR #1263 Strix artifact (`32693700056`) recorded NVIDIA NIM
+ HTTP 429 rate limiting on the primary and retries, followed by a direct OpenAI fallback HTTP
+ 404 for `openai-direct/gpt-5.6-luna`. The gate correctly retained the provider-failure signal
+ and did not promote the fallback's zero-finding report to a successful security result.
+- This is external model-provider availability evidence, not proof of a DiskSage source defect or
+ a cloud/data mutation. The central repair remains subject to a fresh authoritative same-head
+ Strix run and protected approvals; DiskSage's local iCloud admission and eviction gates are
+ unaffected and remain fail-closed.
From 59057c08eb5017ac57b640419a0c7e4779f443d7 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 15:47:19 +0900
Subject: [PATCH 15/86] docs: record latest icloud indexing backlog
---
.../adr/0006-redacted-icloud-health-evidence.md | 13 +++++++++++++
docs/product-technical-gap-baseline.md | 12 ++++++++++++
2 files changed, 25 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 7149b85f9..9a059d2f9 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -173,3 +173,16 @@ status continued to report `client_state=needs-sync` with `needs-sync-up|in-sync
This increasing aggregate queue is stronger provider-stall evidence but still cannot identify the
seven Finder items or prove a cloud write. The observation remains read-only and keeps
`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
+
+## Operational evidence update — 2026-08-24 15:46
+
+The latest bounded read-only receipt still reported `evidence_complete=true` and
+`new_copy_admission_state=blocked`. The sync-up backlog remained 343 items and the active upload
+remained at 95.24%; one active download remained. File Provider pending indexable items increased
+again to 130,571, while disk import, transfer activity, and the 28 filename/2 root exclusions
+remained present. Native status continued to report `client_state=needs-sync` with sync-up pending.
+
+The growing aggregate backlog is consistent with the Finder “preparing to copy” stall, but it does
+not identify the seven Finder items or attest a cloud write. DiskSage therefore continues to keep
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`;
+the probe performed no Finder, provider, source, or cloud mutation.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 1be5dc312..a09332457 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1145,6 +1145,18 @@ checks are not reused:
`provider-sync-incomplete`, copy/attestation/source eviction fail-closed, and performed no
Finder, provider, source, or cloud mutation.
+## 2026-08-24 15:46 +0900 iCloud indexing backlog continues to rise
+
+- A fresh bounded read-only probe reported `evidence_complete=true` and
+ `new_copy_admission_state=blocked`: 343 uploads remain blocked on sync-up, one upload remains
+ active at 95.24%, and one download remains active. File Provider pending indexable items reached
+ 130,571, with disk import/transfer activity and the 28 filename/2 root exclusions still present;
+ native status remains `client_state=needs-sync` with sync-up pending.
+- This is aggregate provider reconciliation evidence that explains the Finder “preparing to copy”
+ symptom but does not identify the seven items or prove remote upload. DiskSage keeps
+ `provider-sync-incomplete`, copy/attestation/source eviction fail-closed, and performs no
+ Finder, provider, source, or cloud mutation.
+
## 2026-08-24 15:42 +0900 Strix provider evidence separated from source readiness
- The exact-head central `.github` PR #1263 Strix artifact (`32693700056`) recorded NVIDIA NIM
From d2da212ffaa88c169ab999586b836e6fe362379a Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:02:48 +0900
Subject: [PATCH 16/86] docs: refresh exact-head review queue
---
.../adr/0006-redacted-icloud-health-evidence.md | 7 +++++++
docs/product-technical-gap-baseline.md | 13 +++++++++++++
2 files changed, 20 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 9a059d2f9..fa8eb27a2 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -186,3 +186,10 @@ The growing aggregate backlog is consistent with the Finder “preparing to copy
not identify the seven Finder items or attest a cloud write. DiskSage therefore continues to keep
`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`;
the probe performed no Finder, provider, source, or cloud mutation.
+
+## Decision maintenance — 2026-08-24 16:03
+
+The latest product review queue keeps the same safety decision: #247 is ready for review at
+`59057c08eb5017ac57b640419a0c7e4779f443d7`, but queued checks and protected approvals are not yet
+complete. The health evidence remains diagnostic only; no readiness, review, or queue state can
+promote aggregate iCloud evidence into per-item upload attestation or local-eviction authority.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index a09332457..148e14058 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1167,3 +1167,16 @@ checks are not reused:
a cloud/data mutation. The central repair remains subject to a fresh authoritative same-head
Strix run and protected approvals; DiskSage's local iCloud admission and eviction gates are
unaffected and remain fail-closed.
+
+## 2026-08-24 16:03 +0900 exact-head review queue refresh
+
+- DiskSage #227 is ready for review at `fd841e9e6b76dc2d47d62d2fddabe53eecf544b2`; its current
+ review threads are resolved, macOS bound-root passed, and the remaining hosted checks plus the
+ two independent protected approvals are still required.
+- DiskSage #247 is ready for review at `59057c08eb5017ac57b640419a0c7e4779f443d7`; the iCloud
+ indexing evidence and customer-facing admission messages are in the exact ancestry. Checks are
+ queued and no protected approval is present.
+- DiskSage #246 is ready for review at `308be49b56d1c38fbe9a5c00ab46ac2b3e51df73`; frontend
+ accessibility/Storybook checks were locally verified, while its hosted Strix result remains an
+ external provider gate that must be freshly revalidated. #244 remains open/non-draft with its
+ pinned Rust baseline checks queued. No merge is inferred from readiness or queued checks.
From 1535320c2b8b288376d9dcd35485a2af58374873 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:04:07 +0900
Subject: [PATCH 17/86] docs: record latest icloud health recheck
---
.../adr/0006-redacted-icloud-health-evidence.md | 12 ++++++++++++
docs/product-technical-gap-baseline.md | 9 +++++++++
2 files changed, 21 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index fa8eb27a2..db38aee02 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -193,3 +193,15 @@ The latest product review queue keeps the same safety decision: #247 is ready fo
`59057c08eb5017ac57b640419a0c7e4779f443d7`, but queued checks and protected approvals are not yet
complete. The health evidence remains diagnostic only; no readiness, review, or queue state can
promote aggregate iCloud evidence into per-item upload attestation or local-eviction authority.
+
+## Operational evidence update — 2026-08-24 16:03
+
+The next bounded read-only receipt still reports `evidence_complete=true` and
+`new_copy_admission_state=blocked`. The sync-up backlog remains 343 items; one upload remains at
+95.24% and one download remains active. Pending File Provider indexable items reached 131,214,
+with disk import, transfer activity, and the 28 filename/2 root exclusions still present. Native
+status remains `client_state=needs-sync` with `needs-sync-up`.
+
+The aggregate queue continues to grow, but the receipt still does not identify the seven Finder
+items or attest a remote write. `provider_sync_attested=false`, `local_eviction_authorized=false`,
+and `mutation_performed=false` remain invariant.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 148e14058..fa1c9dec3 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1180,3 +1180,12 @@ checks are not reused:
accessibility/Storybook checks were locally verified, while its hosted Strix result remains an
external provider gate that must be freshly revalidated. #244 remains open/non-draft with its
pinned Rust baseline checks queued. No merge is inferred from readiness or queued checks.
+
+## 2026-08-24 16:03 +0900 iCloud health recheck
+
+- The bounded probe remains fail-closed: `evidence_complete=true`, `new_copy_admission_state=blocked`,
+ 343 sync-up items blocked, one upload at 95.24%, one download active, and native
+ `client_state=needs-sync`/`needs-sync-up`.
+- File Provider pending indexable items reached 131,214; disk import, transfer activity, and the
+ 28 filename/2 root exclusions remain. This is aggregate reconciliation evidence, not proof that
+ the seven Finder items were uploaded. No provider, Finder, source, or cloud mutation occurred.
From f3562dc2d0bf6b267a372b45690ee9c0bb45d458 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:08:17 +0900
Subject: [PATCH 18/86] docs: record current review repairs
---
docs/product-technical-gap-baseline.md | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index fa1c9dec3..b339972b1 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1189,3 +1189,14 @@ checks are not reused:
- File Provider pending indexable items reached 131,214; disk import, transfer activity, and the
28 filename/2 root exclusions remain. This is aggregate reconciliation evidence, not proof that
the seven Finder items were uploaded. No provider, Finder, source, or cloud mutation occurred.
+
+## 2026-08-24 16:08 +0900 review metadata and exact-head repair
+
+- DiskSage #244 keeps exact head `13caeb04333e50e57c8a51a11b64aeb131c080b2` with all review threads
+ resolved. Its PR description now matches the supported Dependabot configuration and records the
+ local pinned Rust documentation-test evidence without claiming a full hosted pass; checks and
+ protected approvals remain pending.
+- DiskSage #227 advanced to `bf62ea0d74f077add672d0a193de154bde910b97` with a platform-specific
+ test-warning cleanup; its prior bound-root test passed 4/4 locally and hosted checks restarted.
+- DiskSage #247 remains ready for review at `1535320c2b8b288376d9dcd35485a2af58374873`; its latest
+ iCloud evidence is exact-head and all copy/attestation/eviction mutations remain disabled.
From 27357c4b8d8d53cfa23e8fbb00a21dd4f3799e1a Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:12:28 +0900
Subject: [PATCH 19/86] docs: record cli review repair
---
docs/product-technical-gap-baseline.md | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b339972b1..a3a63cdf9 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1200,3 +1200,11 @@ checks are not reused:
test-warning cleanup; its prior bound-root test passed 4/4 locally and hosted checks restarted.
- DiskSage #247 remains ready for review at `1535320c2b8b288376d9dcd35485a2af58374873`; its latest
iCloud evidence is exact-head and all copy/attestation/eviction mutations remain disabled.
+
+## 2026-08-24 16:12 +0900 CLI review repair
+
+- DiskSage #212 advanced to exact head `81c44e43205f21276c39f055f1878805f36e1072` and is ready for
+ review. Its mixed help-plus-invalid CLI test now preserves HOME so it exercises argument parsing,
+ while standalone help remains environment-independent; the targeted cloud-cli test passed 2/2.
+- The provider OAuth environment contract remains intentionally in the default test matrix, and
+ its informational review thread was resolved without adding cloud credentials or side effects.
From 7eca4446c4571e09e1cc6e72350e2aea66022d7e Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:13:20 +0900
Subject: [PATCH 20/86] docs: record worktree audit queue status
---
docs/product-technical-gap-baseline.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index a3a63cdf9..b9f530983 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1208,3 +1208,12 @@ checks are not reused:
while standalone help remains environment-independent; the targeted cloud-cli test passed 2/2.
- The provider OAuth environment contract remains intentionally in the default test matrix, and
its informational review thread was resolved without adding cloud credentials or side effects.
+
+## 2026-08-24 16:14 +0900 worktree-audit queue status
+
+- DiskSage #249 is now ready for review at `44390608d30417477f6a66601b18a53ca87b0a9c`; its
+ non-Strix checks passed in the last exact-head run, while Strix remains an external provider
+ availability failure requiring a fresh authoritative run.
+- The PR is not merge-ready until that provider gate, current coverage, and protected review quorum
+ are satisfied. No worktree, source, provider, or cloud mutation was performed by this status
+ update.
From 4470c9944aef0aa71a1b2a051f87576d8f5956e3 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:15:00 +0900
Subject: [PATCH 21/86] docs: record Homebrew review status
---
docs/product-technical-gap-baseline.md | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b9f530983..48362b80f 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1217,3 +1217,8 @@ checks are not reused:
- The PR is not merge-ready until that provider gate, current coverage, and protected review quorum
are satisfied. No worktree, source, provider, or cloud mutation was performed by this status
update.
+
+## 2026-08-24 16:14 +0900 Homebrew execution stack review status
+
+- DiskSage #205 (Intel Homebrew executable admission) is ready at `5c86668a6e503a174ff0b07151f67226b39547ff`; its hosted Test/Release/build checks are green but the stacked base and protected approvals remain.
+- DiskSage #206 (content-bound Homebrew execution) is ready at `2e7b845b7610a871ec5981d964bcab5cb99df41d`; GitHub reports clean and hosted Test/Release/build checks are green. No approval bypass or merge was performed.
From 1d50cfdc3bc51e315410da5305fe2d9c2582c998 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:16:28 +0900
Subject: [PATCH 22/86] docs: record customer UI queue status
---
docs/product-technical-gap-baseline.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 48362b80f..8db5d2dc0 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1222,3 +1222,10 @@ checks are not reused:
- DiskSage #205 (Intel Homebrew executable admission) is ready at `5c86668a6e503a174ff0b07151f67226b39547ff`; its hosted Test/Release/build checks are green but the stacked base and protected approvals remain.
- DiskSage #206 (content-bound Homebrew execution) is ready at `2e7b845b7610a871ec5981d964bcab5cb99df41d`; GitHub reports clean and hosted Test/Release/build checks are green. No approval bypass or merge was performed.
+
+## 2026-08-24 16:15 +0900 customer-facing UI queue status
+
+- DiskSage #203 (assistive table labels) is ready at `9d573f04145eb4168098623042484fdf73c2ab74`;
+ #202 (bounded scan/navigation failure feedback) is ready at
+ `1d005586b270ca1fcad445970cf44bf5e7268425`. Both have no unresolved review threads; protected
+ checks and approvals remain the merge gates.
From c0b9af08e982f6018042c50015d30811bcede172 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:17:57 +0900
Subject: [PATCH 23/86] docs: record homebrew status verification
---
docs/product-technical-gap-baseline.md | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 8db5d2dc0..636a46647 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1229,3 +1229,9 @@ checks are not reused:
#202 (bounded scan/navigation failure feedback) is ready at
`1d005586b270ca1fcad445970cf44bf5e7268425`. Both have no unresolved review threads; protected
checks and approvals remain the merge gates.
+
+## 2026-08-24 16:18 +0900 Homebrew status UI verification
+
+- DiskSage #189 is ready at exact head `66d7aa767d416048a752c5c550e8d64e03213e0e`; the local
+ frontend regression slice passed 7/7 (`fmt` and `verdictBadge`), while coverage-source-tree and
+ protected approvals remain pending.
From a2bc9205f1b7a0dabeae8def9c65a2e365b67d0c Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:23:50 +0900
Subject: [PATCH 24/86] docs: record current icloud provider backlog
---
.../adr/0006-redacted-icloud-health-evidence.md | 14 ++++++++++++++
docs/product-technical-gap-baseline.md | 12 ++++++++++++
2 files changed, 26 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index db38aee02..b5f2d6f50 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -205,3 +205,17 @@ status remains `client_state=needs-sync` with `needs-sync-up`.
The aggregate queue continues to grow, but the receipt still does not identify the seven Finder
items or attest a remote write. `provider_sync_attested=false`, `local_eviction_authorized=false`,
and `mutation_performed=false` remain invariant.
+
+## Operational evidence update — 2026-08-24 16:21
+
+The latest bounded read-only receipt still reports `evidence_complete=true` and
+`new_copy_admission_state=blocked`. The sync-up backlog remains 343 items; one upload remains
+active at 95.24% and one download remains active. File Provider pending indexable items increased
+to 132,783, while disk-import, transfer, filename-exclusion, and root-exclusion notices remain.
+Native status remains `client_state=needs-sync` with sync-up pending.
+
+This is provider-global reconciliation evidence consistent with Finder remaining at “preparing to
+copy”, but it neither identifies the seven items nor proves that DiskSage is holding a Finder lock
+or that a cloud write completed. `provider_sync_attested=false`, `local_eviction_authorized=false`,
+and `mutation_performed=false` remain required; no Finder, provider, source, or cloud mutation was
+performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 636a46647..8763d7bc4 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1235,3 +1235,15 @@ checks are not reused:
- DiskSage #189 is ready at exact head `66d7aa767d416048a752c5c550e8d64e03213e0e`; the local
frontend regression slice passed 7/7 (`fmt` and `verdictBadge`), while coverage-source-tree and
protected approvals remain pending.
+
+## 2026-08-24 16:21 +0900 iCloud Finder copy remains provider-blocked
+
+- The bounded read-only health receipt still reports `evidence_complete=true` and
+ `new_copy_admission_state=blocked`: 343 uploads remain blocked on sync-up, one upload remains
+ active at 95.24%, and one download remains active. File Provider pending indexable items reached
+ 132,783; disk-import/transfer activity and the 28 filename/2 root exclusions remain, and native
+ status remains `client_state=needs-sync` with sync-up pending.
+- This explains a multi-hour Finder “preparing to copy” symptom as provider-global reconciliation
+ pressure, but does not prove that DiskSage itself holds a Finder lock, identify the seven items,
+ or prove a cloud write. The product keeps `provider-sync-incomplete`, copy/attestation/source
+ eviction fail-closed and performs no Finder, provider, source, or cloud mutation.
From 214951a7cc9ffbd104260d3ba0d68784ff686d20 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:33:07 +0900
Subject: [PATCH 25/86] docs: record exact-head review repairs
---
.../adr/0006-redacted-icloud-health-evidence.md | 8 ++++++++
docs/product-technical-gap-baseline.md | 11 +++++++++++
2 files changed, 19 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index b5f2d6f50..bfa7c70ef 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -219,3 +219,11 @@ copy”, but it neither identifies the seven items nor proves that DiskSage is h
or that a cloud write completed. `provider_sync_attested=false`, `local_eviction_authorized=false`,
and `mutation_performed=false` remain required; no Finder, provider, source, or cloud mutation was
performed.
+
+## Decision maintenance — 2026-08-24 16:32
+
+The exact-head review loop repaired two independent safety/documentation findings without changing
+the iCloud fail-closed decision: #246 restored the coverage dead-code allowance to
+`node_navigation` (head `1972614`), and #227 renamed the bound audit parameter to `stable_root`
+(head `5ad1197`) while retaining the intentionally nested private module contract. Both focused
+Rust test slices passed locally; hosted checks and protected approvals remain authoritative gates.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 8763d7bc4..fcbb065b6 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1247,3 +1247,14 @@ checks are not reused:
pressure, but does not prove that DiskSage itself holds a Finder lock, identify the seven items,
or prove a cloud write. The product keeps `provider-sync-incomplete`, copy/attestation/source
eviction fail-closed and performs no Finder, provider, source, or cloud mutation.
+
+## 2026-08-24 16:32 +0900 exact-head review repairs
+
+- DiskSage #246 advanced to `1972614`; its coverage configuration now keeps the
+ `node_navigation` dead-code allowance without duplicating the attribute on
+ `preferred_scan_roots`. The pinned Rust 1.97.1 navigation slice passed 6/6 and the Devin thread
+ is resolved.
+- DiskSage #227 advanced to `5ad1197`; the bound-root audit parameter now says `stable_root`, and
+ the intentional `duplicate_audit::bound_read_root` module contract was documented. The pinned
+ Rust 1.97.1 duplicate-audit slice passed 10/10 and both current informational threads are
+ resolved. Hosted checks and protected approvals still gate merge.
From fe9514c23c27f1057f923f143b4263f4756a00c4 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:48:23 +0900
Subject: [PATCH 26/86] docs: record worktree test cleanup
---
.../adr/0006-redacted-icloud-health-evidence.md | 7 +++++++
docs/product-technical-gap-baseline.md | 10 ++++++++++
2 files changed, 17 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index bfa7c70ef..9ef2a9eec 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -227,3 +227,10 @@ the iCloud fail-closed decision: #246 restored the coverage dead-code allowance
`node_navigation` (head `1972614`), and #227 renamed the bound audit parameter to `stable_root`
(head `5ad1197`) while retaining the intentionally nested private module contract. Both focused
Rust test slices passed locally; hosted checks and protected approvals remain authoritative gates.
+
+## Decision maintenance — 2026-08-24 16:47
+
+The exact-head loop also repaired #249's process-test storage gap at head `db95c54`: the three
+feature-gated Git-worktree CLI integration tests now reuse deterministic private target directories
+and remove stale output before each nested build, preventing process-id-named target accumulation.
+This test-only cleanup does not alter provider, source, Finder, or cloud mutation boundaries.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index fcbb065b6..b76fcff0f 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1258,3 +1258,13 @@ checks are not reused:
the intentional `duplicate_audit::bound_read_root` module contract was documented. The pinned
Rust 1.97.1 duplicate-audit slice passed 10/10 and both current informational threads are
resolved. Hosted checks and protected approvals still gate merge.
+
+## 2026-08-24 16:47 +0900 Git-worktree test artifact cleanup
+
+- DiskSage #249 advanced to exact head `db95c54` and is ready for review. Its three feature-gated
+ CLI integration tests now reuse deterministic private Cargo target directories and remove stale
+ output before nested builds, closing the repeated-test disk accumulation gap. The affected test
+ targets compile under pinned Rust 1.97.1; the help process slice passed 8/8 before this cleanup.
+- The metadata-failure diagnostic remains a bounded generic fallback by design; it does not expose
+ paths or weaken the fail-closed private-report contract. Current hosted checks and protected
+ approvals remain required.
From 8e960cbb5183c1142a2f7e23b16843bc6a2e1869 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:50:21 +0900
Subject: [PATCH 27/86] docs: refresh exact-head review queue
---
.../adr/0006-redacted-icloud-health-evidence.md | 7 +++++++
docs/product-technical-gap-baseline.md | 7 +++++++
2 files changed, 14 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 9ef2a9eec..613cccb73 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -234,3 +234,10 @@ The exact-head loop also repaired #249's process-test storage gap at head `db95c
feature-gated Git-worktree CLI integration tests now reuse deterministic private target directories
and remove stale output before each nested build, preventing process-id-named target accumulation.
This test-only cleanup does not alter provider, source, Finder, or cloud mutation boundaries.
+
+## Decision maintenance — 2026-08-24 16:50
+
+The current-head review queue was refreshed after the accessibility and compiler-baseline PRs were
+marked ready: #203 is at `5f0bd51`, #244 at `13caeb0`, and #249 at `db95c54`. All remain blocked by
+live hosted gates and protected approvals; none of these states changes the provider evidence
+decision or authorizes source/cloud mutation.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b76fcff0f..3970128a6 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1268,3 +1268,10 @@ checks are not reused:
- The metadata-failure diagnostic remains a bounded generic fallback by design; it does not expose
paths or weaken the fail-closed private-report contract. Current hosted checks and protected
approvals remain required.
+
+## 2026-08-24 16:50 +0900 exact-head queue refresh
+
+- #203 is ready at `5f0bd51` with the current TopFiles accessibility contract; #244 is ready at
+ `13caeb0`; and #249 is ready at `db95c54` after the test-artifact cleanup. Their review threads
+ are resolved where applicable, but current hosted checks and the protected independent-approval
+ quorum remain merge gates. No merge or approval bypass was performed.
From 9137dc6b5445a323d4048a6647d665db32c6fed2 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:51:08 +0900
Subject: [PATCH 28/86] docs: record current icloud backlog
---
.../adr/0006-redacted-icloud-health-evidence.md | 9 +++++++++
docs/product-technical-gap-baseline.md | 10 ++++++++++
2 files changed, 19 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 613cccb73..f9d03e9a0 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -241,3 +241,12 @@ The current-head review queue was refreshed after the accessibility and compiler
marked ready: #203 is at `5f0bd51`, #244 at `13caeb0`, and #249 at `db95c54`. All remain blocked by
live hosted gates and protected approvals; none of these states changes the provider evidence
decision or authorizes source/cloud mutation.
+
+## Operational evidence update — 2026-08-24 16:50
+
+The latest bounded read-only receipt still reports `evidence_complete=true` and
+`new_copy_admission_state=blocked`. The 343-item sync-up backlog, one active upload at 95.24%, one
+active download, native `client_state=needs-sync`, and sync-up pending remain unchanged. Pending
+File Provider indexable items increased to 135,334. The receipt remains aggregate provider evidence
+only; `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+`mutation_performed=false` remain invariant.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 3970128a6..ee4983a40 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1275,3 +1275,13 @@ checks are not reused:
`13caeb0`; and #249 is ready at `db95c54` after the test-artifact cleanup. Their review threads
are resolved where applicable, but current hosted checks and the protected independent-approval
quorum remain merge gates. No merge or approval bypass was performed.
+
+## 2026-08-24 16:50 +0900 iCloud backlog remains the active customer blocker
+
+- The bounded probe now reports File Provider pending indexable items at 135,334, up from 132,783
+ at 16:21; 343 uploads remain blocked on sync-up, one upload remains active at 95.24%, and one
+ download remains active. Native status remains `client_state=needs-sync` with sync-up pending and
+ `new_copy_admission_state=blocked`.
+- The growing queue is consistent with Finder’s multi-hour “preparing to copy” state, but still
+ does not prove DiskSage holds a Finder lock or identify the seven items. No provider, source,
+ Finder, or cloud mutation was performed, and local eviction remains fail-closed.
From 6b31fbc408ef2b89c6460c7da54f665e4b9ec8f4 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 16:55:05 +0900
Subject: [PATCH 29/86] docs: record concurrent test target repair
---
.../adr/0006-redacted-icloud-health-evidence.md | 6 ++++++
docs/product-technical-gap-baseline.md | 6 ++++++
2 files changed, 12 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index f9d03e9a0..49ec89656 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -250,3 +250,9 @@ active download, native `client_state=needs-sync`, and sync-up pending remain un
File Provider indexable items increased to 135,334. The receipt remains aggregate provider evidence
only; `provider_sync_attested=false`, `local_eviction_authorized=false`, and
`mutation_performed=false` remain invariant.
+
+## Decision maintenance — 2026-08-24 16:55
+
+The current #249 exact head is `aa5c37d`. Its test-only target helper now keeps concurrent
+process-scoped build directories while pruning dead-process or aged stale output; this preserves
+the disk-reclamation goal without changing any provider, Finder, source, or cloud mutation rule.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index ee4983a40..c1be9dfbb 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1285,3 +1285,9 @@ checks are not reused:
- The growing queue is consistent with Finder’s multi-hour “preparing to copy” state, but still
does not prove DiskSage holds a Finder lock or identify the seven items. No provider, source,
Finder, or cloud mutation was performed, and local eviction remains fail-closed.
+
+## 2026-08-24 16:55 +0900 concurrent test-target repair
+
+- #249 advanced to exact head `aa5c37d`; its shared test helper now uses process-scoped Cargo target
+ directories and prunes stale outputs without deleting another active run. The affected targets
+ compile under pinned Rust 1.97.1, and the current hosted checks have restarted for this head.
From a12e6b220d3f308f3ed730ce0c8ce09c1f7471ac Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:01:00 +0900
Subject: [PATCH 30/86] docs: record live brctl Finder stall evidence
---
.../adr/0006-redacted-icloud-health-evidence.md | 16 ++++++++++++++++
docs/product-technical-gap-baseline.md | 17 +++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 49ec89656..e6757d9cd 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -256,3 +256,19 @@ only; `provider_sync_attested=false`, `local_eviction_authorized=false`, and
The current #249 exact head is `aa5c37d`. Its test-only target helper now keeps concurrent
process-scoped build directories while pruning dead-process or aged stale output; this preserves
the disk-reclamation goal without changing any provider, Finder, source, or cloud mutation rule.
+
+## Operational evidence update — 2026-08-24 17:00
+
+A fresh read-only `/usr/bin/brctl status` completed at 17:00. The iCloud container reports
+`client:needs-sync` and `sync:needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`; the
+bounded summary contains 1,740 `pending-scan` entries, 343 `pending-sync-up` entries, 1,807
+scheduled sync-up markers, and 5 upload errors. Several queued uploads have not run for roughly
+60–66 hours, including `CKErrorDomain:4` “Saving asset failed” records.
+
+This is provider-global reconciliation/error evidence consistent with the Finder
+`real_datasets` “복사 준비 중” dialog persisting for hours. It does not identify the seven Finder
+items or attest a cloud write, so the evidence remains diagnostic only:
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
+DiskSage must continue to expose only the explicit bounded Finder-cancel action and must not
+restart provider processes or mutate Finder, source, or cloud state automatically. The root volume
+had about 36 GiB available at the same observation, so disk-full is not the current root cause.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index c1be9dfbb..b919808ff 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1291,3 +1291,20 @@ checks are not reused:
- #249 advanced to exact head `aa5c37d`; its shared test helper now uses process-scoped Cargo target
directories and prunes stale outputs without deleting another active run. The affected targets
compile under pinned Rust 1.97.1, and the current hosted checks have restarted for this head.
+
+## 2026-08-24 17:00 +0900 live brctl confirmation of the Finder stall
+
+- A fresh read-only `/usr/bin/brctl status` completed at 17:00. The iCloud container still reports
+ `client:needs-sync` and `sync:needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`; the
+ dump contains 1,740 `pending-scan` entries, 343 `pending-sync-up` entries, 1,807 scheduled
+ sync-up markers, and 5 upload errors. Individual queued uploads last ran roughly 60–66 hours
+ ago, including `CKErrorDomain:4` / “Saving asset failed” records.
+- This is stronger provider-global evidence for the screenshot's multi-hour `real_datasets`
+ “복사 준비 중” state, but it still cannot identify the seven Finder items or prove a cloud
+ write. DiskSage performed no Finder/provider/source/cloud mutation; `provider-sync-incomplete`,
+ copy/attestation, and local-eviction gates remain fail-closed. The root volume currently has
+ about 36 GiB available, so the live blocker is provider reconciliation/error backlog rather
+ than a full root volume.
+- Exact-head review evidence remains current: #249 is now `6b95c59` after centralizing the CLI's
+ reference validation in the library; #246 is `1972614`; #227 is `5ad1197`. Hosted checks and
+ protected independent approvals remain the only merge gates.
From 8d6de50bd6f1d343df745fe435f87824c1d952f6 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:02:44 +0900
Subject: [PATCH 31/86] docs: record long-lived Finder provider session
---
docs/architecture/adr/0006-redacted-icloud-health-evidence.md | 3 +++
docs/product-technical-gap-baseline.md | 3 +++
2 files changed, 6 insertions(+)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index e6757d9cd..8e9b4e1e8 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -272,3 +272,6 @@ items or attest a cloud write, so the evidence remains diagnostic only:
DiskSage must continue to expose only the explicit bounded Finder-cancel action and must not
restart provider processes or mutate Finder, source, or cloud state automatically. The root volume
had about 36 GiB available at the same observation, so disk-full is not the current root cause.
+At 17:02, a read-only process inventory showed Finder (PID 1422), `fileproviderd` (1450), and
+`bird` (1462) all started at 10:43:49, about 6h18m earlier. This confirms a long-lived provider
+session but does not establish DiskSage ownership or a Finder lock.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b919808ff..f9b3f340a 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1305,6 +1305,9 @@ checks are not reused:
copy/attestation, and local-eviction gates remain fail-closed. The root volume currently has
about 36 GiB available, so the live blocker is provider reconciliation/error backlog rather
than a full root volume.
+- At 17:02, the read-only process inventory showed Finder (PID 1422), `fileproviderd` (1450), and
+ `bird` (1462) all started at 10:43:49, about 6h18m earlier. This confirms a long-lived provider
+ session, not that DiskSage owns or has locked the Finder operation.
- Exact-head review evidence remains current: #249 is now `6b95c59` after centralizing the CLI's
reference validation in the library; #246 is `1972614`; #227 is `5ad1197`. Hosted checks and
protected independent approvals remain the only merge gates.
From e057ac0b9ea1cc99b7f934a98f852143c3b360ae Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:06:44 +0900
Subject: [PATCH 32/86] docs: refresh exact-head PR inventory
---
docs/product-technical-gap-baseline.md | 36 +++++++++++++-------------
1 file changed, 18 insertions(+), 18 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index f9b3f340a..0b518ee25 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 13:58 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 13:58 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:05 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:05 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-22 04:23 +0900 current protected PR inventory
+## 2026-08-24 17:05 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -23,21 +23,21 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
-| #249 | `44390608d30417477f6a66601b18a53ca87b0a9c` | yes | blocked | review required | Git worktree audit help; Strix provider-prefix failure is tracked against central `.github` PR #1263 |
-| #247 | `7c690c09a8409f5aafdc880d188fcc2939c14ce3` | no | dirty | review required | destination-staging headroom binding and provider-scoped stall-history repair; hosted checks/review rerunning |
-| #246 | `476678c150ded97b400d62566292adfff56a84c2` | no | clean | none | Storybook/accessibility contract; approvals still absent |
-| #244 | `b9941295ac354bb63cf911a064a1f4df1f8eb60b` | yes | blocked | review required | Rust 1.97.1 baseline; protected approval quorum absent |
-| #238 | `d44b23bdf4108bf6b6f6378f7e0ac305187deec6` | no | blocked | review required | mail-parser update; all current checks are green but protected approvals are absent |
-| #234 | `22bc81585257f409abf9f99a5db81184e84dafe9` | yes | blocked | review required | ureq update; protected approval quorum absent |
-| #232 | `99db1d36f722aeb00b280793126064e4951e62be` | no | blocked | review required | @types/node update; protected approval quorum absent |
-| #230 | `c7e4e623e9b691dcd6a24cad3cc492393cb5d83e` | yes | blocked | review required | download-artifact update; protected approval quorum absent |
-| #228 | `1eb947ec9d4e591638230a8cb24af4d5b14ae35b` | yes | clean | none | private-evidence identity hardening; review pending |
-| #189 | `1ada64a334fc27a022d42c897fabe32ccc25ae7e` | no | blocked | review required | stacked Homebrew/iCloud safety UI repair; hosted checks are rerunning |
-| #156 | `c6dc8a6635639329c9bb02d9e32d6548abeaa427` | yes | unknown | review required | exact-head coverage/release contracts; coverage measured below 100% |
-
-No protected merge is inferred from `clean`, green predecessor checks, bot comments, or queued
-reviews. The queue is processed exact-head-first: review, repair, recheck, then normal protected
-merge.
+| #249 | `6b95c590b19fe2ecd2104b77d3f87c30a6eedff1` | no | blocked | review required | Git worktree audit help; reference validation is now shared with the library |
+| #247 | `8d6de50bd6f1d343df745fe435f87824c1d952f6` | no | blocked | review required | pending iCloud provider indexing plus live Finder/provider stall evidence |
+| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
+| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
+| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
+| #212 | `75d728e403cf0b30511e149a7e650731f6472733` | no | blocked | review required | cloud operational CLI help |
+| #206 | `2e7b845b7610a871ec5981d964bcab5cb99df41d` | no | clean | none | content-bound Homebrew execution; no qualifying approval |
+| #205 | `5c86668a6e503a174ff0b07151f67226b39547ff` | no | clean | none | Intel Homebrew target support; no qualifying approval |
+| #203 | `5f0bd51be4b2faca8a30aadc661bf651a619c549` | no | blocked | review required | TopFiles accessibility contract |
+| #202 | `ec2db50307d0d6bccd2546a820c7a6822f054df5` | no | blocked | review required | bounded scan/navigation failure feedback |
+| #189 | `66d7aa767d416048a752c5c550e8d64e03213e0e` | no | blocked | review required | Homebrew cleanup status UI |
+
+Additional draft dependency/security PRs remain open and are not merge candidates. No protected
+merge is inferred from `clean`, green predecessor checks, bot comments, or queued reviews. The queue
+is processed exact-head-first: review, repair, recheck, then normal protected merge.
## Buyer-observable product gaps
From 394e8a01e80149bd773bb578b3c7a7cf83591621 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 01:21:58 -0700
Subject: [PATCH 33/86] test: prove mixed native copy headroom is not a
plan-wide blocker
---
...cloud_plan_destination_headroom_runtime.rs | 87 +++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
index fcc20baec..2c3a7c9f0 100644
--- a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -83,3 +83,90 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
"dry-run planning must not materialize the redirected destination",
);
}
+
+#[cfg(unix)]
+#[test]
+fn one_unverified_candidate_does_not_blanket_block_candidates_with_verified_headroom() {
+ use std::os::unix::fs::symlink;
+
+ let fixture = tempfile::tempdir().unwrap();
+ let source_root = fixture.path().join("source");
+ let cloud_root = fixture.path().join("cloud");
+ let archive_root = cloud_root.join("DiskSage Archive");
+ let redirected_documents = fixture.path().join("redirected-documents");
+ std::fs::create_dir(&source_root).unwrap();
+ std::fs::create_dir(&cloud_root).unwrap();
+ std::fs::create_dir(&archive_root).unwrap();
+ std::fs::create_dir(&redirected_documents).unwrap();
+ symlink(&redirected_documents, archive_root.join("documents")).unwrap();
+
+ let observed_at_ms = system_now_ms();
+ let mut facts = Vec::new();
+ for (name, bytes) in [("report.pdf", b"report".as_slice()), ("clip.mp4", b"clip".as_slice())] {
+ let path = source_root.join(name);
+ std::fs::write(&path, bytes).unwrap();
+ let metadata = std::fs::metadata(&path).unwrap();
+ let modified_ms = metadata
+ .modified()
+ .unwrap()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap()
+ .as_millis() as u64;
+ facts.push(FileFact {
+ path,
+ bytes: metadata.len(),
+ created_ms: observed_at_ms,
+ modified_ms,
+ content_metadata: ContentMetadata::default(),
+ });
+ }
+
+ let root = CloudRoot {
+ id: "google-drive:test".into(),
+ provider: CloudProvider::GoogleDrive,
+ account_scope: CloudAccountScope::Personal,
+ label: "Google Drive".into(),
+ path: cloud_root.to_string_lossy().into_owned(),
+ readable: true,
+ access_issue: None,
+ };
+ let report = plan_cloud_archive(
+ &facts,
+ &source_root,
+ &root,
+ observed_at_ms,
+ CloudPlanOptions {
+ min_size_bytes: 1,
+ min_age_days: 0,
+ limit: 10,
+ },
+ );
+
+ assert_eq!(report.candidates.len(), 2);
+ assert!(report.candidates.iter().all(|candidate| candidate.blocked_reason.is_none()));
+ assert!(
+ report
+ .notices
+ .iter()
+ .any(|notice| notice == "local-volume-headroom-partial"),
+ "mixed per-candidate headroom results need a non-blocking plan diagnostic",
+ );
+ assert!(
+ !report
+ .notices
+ .iter()
+ .any(|notice| notice == "local-volume-headroom-unverified"),
+ "one unsafe destination ancestor must not disable candidates whose own staging headroom is verified",
+ );
+ assert!(
+ !report
+ .notices
+ .iter()
+ .any(|notice| notice == "local-volume-headroom-insufficient"),
+ "plan-wide native-copy blockers are reserved for plans where no candidate has verified headroom",
+ );
+ assert!(
+ !redirected_documents.join("report.pdf").exists(),
+ "dry-run planning must not materialize the redirected candidate",
+ );
+}
From 545896524989d1ce79802b525b648cf5250bc4b9 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 01:24:39 -0700
Subject: [PATCH 34/86] test: bind mixed headroom regression to desktop plan
normalization
---
src-tauri/tests/cloud_plan_destination_headroom_runtime.rs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
index 2c3a7c9f0..f3bfc4e13 100644
--- a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -2,6 +2,7 @@ use disksage_lib::cloud::{
plan_cloud_archive, CloudAccountScope, CloudPlanOptions, CloudProvider, CloudRoot, ContentMetadata,
FileFact, system_now_ms,
};
+use disksage_lib::cloud_plan_view::normalize_native_copy_headroom_notices;
#[cfg(unix)]
#[test]
@@ -53,7 +54,7 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
access_issue: None,
};
- let report = plan_cloud_archive(
+ let mut report = plan_cloud_archive(
&[file],
&source_root,
&root,
@@ -64,6 +65,7 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
limit: 10,
},
);
+ normalize_native_copy_headroom_notices(&mut report);
assert_eq!(report.candidates.len(), 1);
assert_eq!(report.candidates[0].blocked_reason, None);
@@ -130,7 +132,7 @@ fn one_unverified_candidate_does_not_blanket_block_candidates_with_verified_head
readable: true,
access_issue: None,
};
- let report = plan_cloud_archive(
+ let mut report = plan_cloud_archive(
&facts,
&source_root,
&root,
@@ -141,6 +143,7 @@ fn one_unverified_candidate_does_not_blanket_block_candidates_with_verified_head
limit: 10,
},
);
+ normalize_native_copy_headroom_notices(&mut report);
assert_eq!(report.candidates.len(), 2);
assert!(report.candidates.iter().all(|candidate| candidate.blocked_reason.is_none()));
From c5eb31729e471d1dd49be67527b90551509e9d05 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 01:25:32 -0700
Subject: [PATCH 35/86] fix: keep mixed native copy headroom from
blanket-blocking the plan
---
src-tauri/src/cloud_plan_view.rs | 58 ++++++++++++++++++++++++++++++--
1 file changed, 56 insertions(+), 2 deletions(-)
diff --git a/src-tauri/src/cloud_plan_view.rs b/src-tauri/src/cloud_plan_view.rs
index 052b52428..261a07eae 100644
--- a/src-tauri/src/cloud_plan_view.rs
+++ b/src-tauri/src/cloud_plan_view.rs
@@ -14,6 +14,59 @@ use crate::cloud_transfer::{
};
use crate::provider_capacity::CloudCapacityAssessment;
use crate::volume_pressure::LocalVolumeSnapshot;
+use std::path::Path;
+
+const PLAN_WIDE_HEADROOM_BLOCKERS: [&str; 2] = [
+ "local-volume-headroom-insufficient",
+ "local-volume-headroom-unverified",
+];
+const PARTIAL_HEADROOM_NOTICE: &str = "local-volume-headroom-partial";
+
+/// Keep the desktop plan's plan-wide headroom notices honest at candidate granularity.
+///
+/// The core planner records a stable aggregate notice whenever any candidate's destination probe
+/// fails. The desktop historically treated those aggregate notices as blanket copy-button gates,
+/// even though mutation-time native copy revalidates headroom for the selected candidate. If at
+/// least one otherwise-unblocked candidate has verified destination/staging headroom, replace the
+/// blanket blocker with a non-blocking partial diagnostic. Plans where no candidate can establish
+/// headroom keep the original fail-closed blocker. This does not grant mutation authority: the
+/// per-candidate copy boundary still performs the authoritative probe immediately before staging.
+pub fn normalize_native_copy_headroom_notices(report: &mut CloudPlanReport) {
+ if !report
+ .notices
+ .iter()
+ .any(|notice| PLAN_WIDE_HEADROOM_BLOCKERS.contains(¬ice.as_str()))
+ {
+ return;
+ }
+
+ let has_verified_candidate = report
+ .candidates
+ .iter()
+ .filter(|candidate| candidate.blocked_reason.is_none())
+ .any(|candidate| {
+ crate::copy_headroom::require_destination_copy_headroom(
+ Path::new(&candidate.dst),
+ candidate.bytes,
+ report.generated_at_ms,
+ )
+ .is_ok()
+ });
+ if !has_verified_candidate {
+ return;
+ }
+
+ report
+ .notices
+ .retain(|notice| !PLAN_WIDE_HEADROOM_BLOCKERS.contains(¬ice.as_str()));
+ if !report
+ .notices
+ .iter()
+ .any(|notice| notice == PARTIAL_HEADROOM_NOTICE)
+ {
+ report.notices.push(PARTIAL_HEADROOM_NOTICE.into());
+ }
+}
/// One cloud candidate plus the backend-authored approval presentation for its current state.
#[derive(Debug, Clone, serde::Serialize)]
@@ -23,7 +76,7 @@ pub struct CloudPlanCandidateView {
pub candidate: CloudCandidate,
/// Exact action available for this candidate, or `None` when another blocker applies.
pub copy_approval_action: Option,
- /// Candidate-specific confirmation phrase generated by Rust for the available action.
+ /// Candidate-specific confirmation phrase generated by Rust, or null when blocked.
pub exact_copy_approval_phrase: Option,
/// Maximum age, in milliseconds, accepted for an approval created from this plan.
pub copy_approval_max_age_ms: u64,
@@ -79,7 +132,8 @@ pub struct CloudPlanReportView {
}
impl From for CloudPlanReportView {
- fn from(report: CloudPlanReport) -> Self {
+ fn from(mut report: CloudPlanReport) -> Self {
+ normalize_native_copy_headroom_notices(&mut report);
let CloudPlanReport {
cloud_root,
generated_at_ms,
From 48399b8c3ed2a02289cd56ffbe31d80eee2ad6c6 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:33:42 +0900
Subject: [PATCH 36/86] docs: record audit test disk-pressure repair
---
.../adr/0006-redacted-icloud-health-evidence.md | 9 +++++++++
docs/product-technical-gap-baseline.md | 8 ++++----
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 8e9b4e1e8..78c686a31 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -275,3 +275,12 @@ had about 36 GiB available at the same observation, so disk-full is not the curr
At 17:02, a read-only process inventory showed Finder (PID 1422), `fileproviderd` (1450), and
`bird` (1462) all started at 10:43:49, about 6h18m earlier. This confirms a long-lived provider
session but does not establish DiskSage ownership or a Finder lock.
+
+## Decision maintenance — 2026-08-24 17:33
+
+The exact-head PR #249 test repair is now `dc9ccf2`. Its three process-contract tests use Cargo's
+`CARGO_BIN_EXE_disksage-git-worktree-audit` instead of launching nested feature-gated builds;
+the focused slices passed 8/8, 2/2, and 1/1, with no new `disksage-git-worktree-*` temporary
+targets created. This removes a local test-side source of disk pressure without changing the
+provider, Finder, source, or cloud mutation boundaries. The PR is draft, blocked, review-required,
+with hosted checks pending and no unresolved review threads.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 0b518ee25..ebeb3e44e 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:05 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:05 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:33 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:33 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:05 +0900 current protected PR inventory
+## 2026-08-24 17:33 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -23,7 +23,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
-| #249 | `6b95c590b19fe2ecd2104b77d3f87c30a6eedff1` | no | blocked | review required | Git worktree audit help; reference validation is now shared with the library |
+| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
| #247 | `8d6de50bd6f1d343df745fe435f87824c1d952f6` | no | blocked | review required | pending iCloud provider indexing plus live Finder/provider stall evidence |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
From b8a17ebe32320ab796656b3158333643223ad7e2 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:38:03 +0900
Subject: [PATCH 37/86] fix: preserve health stall history across schema
additions
---
src-tauri/src/icloud_sync_health.rs | 84 ++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)
diff --git a/src-tauri/src/icloud_sync_health.rs b/src-tauri/src/icloud_sync_health.rs
index 7e0ad185a..160634086 100644
--- a/src-tauri/src/icloud_sync_health.rs
+++ b/src-tauri/src/icloud_sync_health.rs
@@ -337,6 +337,34 @@ fn health_evidence_fingerprint(
Ok(digest.iter().map(|byte| format!("{byte:02x}")).collect())
}
+/// Recompute the pre-`pending_indexable_count` fingerprint without changing field order.
+///
+/// Retained snapshots are immutable evidence. Accepting this one historical encoding keeps an
+/// upgrade from silently shortening the durable stall clock while still requiring the exact old
+/// digest; newly written snapshots continue to use `health_evidence_fingerprint`.
+fn health_evidence_fingerprint_without_pending_indexable(
+ snapshot: &IcloudSyncHealthEvidenceSnapshot,
+) -> Result {
+ let mut unsigned = snapshot.clone();
+ unsigned.evidence_fingerprint_sha256.clear();
+ let mut encoded = serde_json::to_vec(&unsigned)
+ .map_err(|_| "icloud-sync-health-evidence-fingerprint-encode-failed".to_string())?;
+ if unsigned
+ .file_provider_activity
+ .as_ref()
+ .is_some_and(|activity| activity.pending_indexable_count.is_none())
+ {
+ let field = b"\"pending_indexable_count\":null,";
+ let index = encoded
+ .windows(field.len())
+ .position(|window| window == field)
+ .ok_or_else(|| "icloud-sync-health-evidence-legacy-field-missing".to_string())?;
+ encoded.drain(index..index + field.len());
+ }
+ let digest = Sha256::digest(encoded);
+ Ok(digest.iter().map(|byte| format!("{byte:02x}")).collect())
+}
+
/// Project a live report into the bounded, path-free durable evidence shape.
pub fn health_evidence_snapshot_from_report(
report: &IcloudSyncHealthReport,
@@ -458,7 +486,16 @@ pub fn validate_icloud_sync_health_evidence_snapshot(
}
}
let expected = health_evidence_fingerprint(snapshot)?;
- if snapshot.evidence_fingerprint_sha256 != expected {
+ let legacy_expected = (snapshot
+ .file_provider_activity
+ .as_ref()
+ .is_some_and(|activity| activity.pending_indexable_count.is_none()))
+ .then(|| health_evidence_fingerprint_without_pending_indexable(snapshot));
+ let fingerprint_matches = snapshot.evidence_fingerprint_sha256 == expected
+ || legacy_expected
+ .and_then(Result::ok)
+ .is_some_and(|value| snapshot.evidence_fingerprint_sha256 == value);
+ if !fingerprint_matches {
return Err("icloud-sync-health-evidence-fingerprint-invalid".into());
}
Ok(())
@@ -2727,6 +2764,51 @@ mod tests {
);
}
+ #[test]
+ fn health_evidence_accepts_pre_pending_indexable_fingerprint() {
+ let mut report = build_report(
+ 1,
+ vec![],
+ IcloudUploadQueueSummary::default(),
+ true,
+ true,
+ )
+ .unwrap();
+ report.file_provider_activity = Some(IcloudFileProviderActivityEvidence {
+ schema_version: ICLOUD_FILE_PROVIDER_ACTIVITY_SCHEMA_VERSION,
+ observed_at_ms: 1,
+ command_succeeded: true,
+ timed_out: false,
+ output_truncated: false,
+ no_progress_fetch_count: 0,
+ no_progress_create_count: 0,
+ materialization_failure_count: 0,
+ staged_item_missing_count: 0,
+ sync_excluded_filename_count: 0,
+ sync_excluded_root_count: 0,
+ pending_indexable_count: None,
+ active_upload_count: 0,
+ active_download_count: 0,
+ active_upload_progress_millionths: None,
+ active_download_progress_millionths: None,
+ notices: vec!["test-notice".into()],
+ });
+ let mut snapshot = health_evidence_snapshot_from_report(&report).unwrap();
+ snapshot.evidence_fingerprint_sha256 =
+ health_evidence_fingerprint_without_pending_indexable(&snapshot).unwrap();
+ validate_icloud_sync_health_evidence_snapshot(&snapshot).unwrap();
+
+ snapshot
+ .file_provider_activity
+ .as_mut()
+ .unwrap()
+ .pending_indexable_count = Some(1);
+ assert_eq!(
+ validate_icloud_sync_health_evidence_snapshot(&snapshot).unwrap_err(),
+ "icloud-sync-health-evidence-fingerprint-invalid"
+ );
+ }
+
#[cfg(not(coverage))]
#[test]
fn health_evidence_is_create_only_and_bounded() {
From e551aceb58ccc9d09b756dfb23e4a4e80f45bc52 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:39:12 +0900
Subject: [PATCH 38/86] docs: record upgrade-compatible stall history
---
.../adr/0006-redacted-icloud-health-evidence.md | 8 ++++++++
docs/product-technical-gap-baseline.md | 8 ++++----
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 78c686a31..e239bd7d1 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -284,3 +284,11 @@ the focused slices passed 8/8, 2/2, and 1/1, with no new `disksage-git-worktree-
targets created. This removes a local test-side source of disk pressure without changing the
provider, Finder, source, or cloud mutation boundaries. The PR is draft, blocked, review-required,
with hosted checks pending and no unresolved review threads.
+
+## Decision maintenance — 2026-08-24 17:38
+
+At exact head `b8a17eb`, retained iCloud health snapshots now accept the exact pre-
+`pending_indexable_count` fingerprint encoding when that optional field is absent. New snapshots
+still use the current fingerprint, and the 29-test iCloud health slice passed on Rust 1.97.1. This
+preserves the restart-safe stall clock across upgrades without weakening evidence integrity or
+changing the fail-closed provider/Finder/source/cloud mutation boundary.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index ebeb3e44e..b1fc8d2ee 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:33 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:33 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:38 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:38 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:33 +0900 current protected PR inventory
+## 2026-08-24 17:38 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `8d6de50bd6f1d343df745fe435f87824c1d952f6` | no | blocked | review required | pending iCloud provider indexing plus live Finder/provider stall evidence |
+| #247 | `b8a17ebe32320ab796656b3158333643223ad7e2` | yes | blocked | none | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
From 933ce7ddc65644d41ac3288587f5aca9c3327823 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:40:36 +0900
Subject: [PATCH 39/86] docs: refresh current provider backlog evidence
---
.../adr/0006-redacted-icloud-health-evidence.md | 11 +++++++++++
docs/product-technical-gap-baseline.md | 8 ++++----
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index e239bd7d1..ffb3444ab 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -292,3 +292,14 @@ At exact head `b8a17eb`, retained iCloud health snapshots now accept the exact p
still use the current fingerprint, and the 29-test iCloud health slice passed on Rust 1.97.1. This
preserves the restart-safe stall clock across upgrades without weakening evidence integrity or
changing the fail-closed provider/Finder/source/cloud mutation boundary.
+
+## Operational evidence update — 2026-08-24 17:40
+
+A fresh bounded read-only `/usr/bin/brctl status` still reports `client:needs-sync` with
+`pending-scan=1,740`, `pending-sync-up=343`, and `sync-up-scheduled=2,150`; 20 lines matched the
+bounded upload-error/“Saving asset failed” markers. Finder, `fileproviderd`, and `bird` remain the
+same long-lived provider session started at 10:43:49. The root volume currently has about 21 GiB
+available (926 GiB total, 12 GiB used), so this is not a full-root condition, but headroom is
+lower than the earlier 36 GiB observation. The Finder copy remains diagnostic-only: no item-level
+remote write is identified, and `provider_sync_attested=false`, `local_eviction_authorized=false`,
+and `mutation_performed=false` remain invariant.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b1fc8d2ee..559fb3e25 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:38 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:38 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:40 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:40 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:38 +0900 current protected PR inventory
+## 2026-08-24 17:40 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `b8a17ebe32320ab796656b3158333643223ad7e2` | yes | blocked | none | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
+| #247 | `e551aceb58ccc9d09b756dfb23e4a4e80f45bc52` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
From d9b67a1db38e645ef97ebe9a1ac8d6f7b4541543 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:48:58 +0900
Subject: [PATCH 40/86] test: bind mixed headroom fixture to dated path
---
.../tests/cloud_plan_destination_headroom_runtime.rs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
index f3bfc4e13..eb8bbe4fe 100644
--- a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -1,6 +1,6 @@
use disksage_lib::cloud::{
plan_cloud_archive, CloudAccountScope, CloudPlanOptions, CloudProvider, CloudRoot, ContentMetadata,
- FileFact, system_now_ms,
+ FileFact, production_year_month, system_now_ms,
};
use disksage_lib::cloud_plan_view::normalize_native_copy_headroom_notices;
@@ -100,9 +100,14 @@ fn one_unverified_candidate_does_not_blanket_block_candidates_with_verified_head
std::fs::create_dir(&cloud_root).unwrap();
std::fs::create_dir(&archive_root).unwrap();
std::fs::create_dir(&redirected_documents).unwrap();
- symlink(&redirected_documents, archive_root.join("documents")).unwrap();
let observed_at_ms = system_now_ms();
+ let (year, month) = production_year_month(observed_at_ms);
+ let archive_month = archive_root
+ .join(format!("{year:04}"))
+ .join(format!("{month:02}"));
+ std::fs::create_dir_all(&archive_month).unwrap();
+ symlink(&redirected_documents, archive_month.join("documents")).unwrap();
let mut facts = Vec::new();
for (name, bytes) in [("report.pdf", b"report".as_slice()), ("clip.mp4", b"clip".as_slice())] {
let path = source_root.join(name);
From d5ffedb0872d07d933f7e0b95e42681c8e1c1174 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:49:41 +0900
Subject: [PATCH 41/86] docs: record dated headroom fixture repair
---
.../adr/0006-redacted-icloud-health-evidence.md | 9 +++++++++
docs/product-technical-gap-baseline.md | 8 ++++----
2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index ffb3444ab..d40fe9d2c 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -206,6 +206,15 @@ The aggregate queue continues to grow, but the receipt still does not identify t
items or attest a remote write. `provider_sync_attested=false`, `local_eviction_authorized=false`,
and `mutation_performed=false` remain invariant.
+## Decision maintenance — 2026-08-24 17:49
+
+The exact-head PR #247 integration run exposed and repaired a test-fixture defect in the mixed
+destination-headroom regression. The unsafe symlink is now placed at the actual dated destination
+ancestor derived by the same Rust production-date decomposition used by the planner; the verified
+media candidate remains eligible while the unsafe document candidate remains diagnostically
+partial. The focused suite passed 11/11 on Rust 1.97.1. No provider, Finder, source, or cloud
+mutation rule changed.
+
## Operational evidence update — 2026-08-24 16:21
The latest bounded read-only receipt still reports `evidence_complete=true` and
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 559fb3e25..77b6e9d3c 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:40 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:40 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:49 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:49 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:40 +0900 current protected PR inventory
+## 2026-08-24 17:49 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `e551aceb58ccc9d09b756dfb23e4a4e80f45bc52` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
+| #247 | `d9b67a1db38e645ef97ebe9a1ac8d6f7b4541543` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
From d66ed782eb81649e873d05d933c1df790f176681 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:53:21 +0900
Subject: [PATCH 42/86] docs: refresh exact PR inventory head
---
docs/product-technical-gap-baseline.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 77b6e9d3c..cc99091cb 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:49 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:49 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:52 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:52 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:49 +0900 current protected PR inventory
+## 2026-08-24 17:52 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `d9b67a1db38e645ef97ebe9a1ac8d6f7b4541543` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal |
+| #247 | `d5ffedb0872d07d933f7e0b95e42681c8e1c1174` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal and dated headroom-fixture repair |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
From 7b45ada3be4762724fd2c964b0dafe7f62dbeacb Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 17:59:13 +0900
Subject: [PATCH 43/86] feat: explain Finder cancellation permission
---
src/lib/CloudArchive.svelte | 6 ++++++
src/lib/cloudArchiveAdmissionContract.test.ts | 2 ++
2 files changed, 8 insertions(+)
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index 5a56eac32..c4889ea3f 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -974,6 +974,9 @@
|| icloudHealth.file_provider_activity.active_upload_count > 0
|| icloudHealth.file_provider_activity.active_download_count > 0
)}
+
+ 이 작업은 Finder에 Escape 키를 보내므로 macOS 손쉬운 사용 설정에서 DiskSage의 System Events 제어 권한이 필요합니다. 권한이 없으면 요청만 실패하며 파일·클라우드 데이터는 변경되지 않습니다.
+
@@ -1080,6 +1083,9 @@
{recoveringProvider ? "공급자 앱 재기동 중…" : "공급자 앱 재기동 후 상태 재확인"}
{#if canCancelFinderCopyForProviderGlobalSync(providerGlobalSync)}
+
+ 이 작업은 Finder에 Escape 키를 보내므로 macOS 손쉬운 사용 설정에서 DiskSage의 System Events 제어 권한이 필요합니다. 권한이 없으면 요청만 실패하며 파일·클라우드 데이터는 변경되지 않습니다.
+
diff --git a/src/lib/cloudArchiveAdmissionContract.test.ts b/src/lib/cloudArchiveAdmissionContract.test.ts
index 93d2b2391..0d71fb248 100644
--- a/src/lib/cloudArchiveAdmissionContract.test.ts
+++ b/src/lib/cloudArchiveAdmissionContract.test.ts
@@ -71,6 +71,8 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("provider-global-sync-item-not-found");
expect(source).toContain("cancellingFinderCopy || checkingProviderGlobalSync");
expect(source).toContain("finderCopyCancelStatus = \"Finder 복사 취소 요청을 보냈습니다. 상태를 다시 확인하십시오.\"");
+ expect(source).toContain("macOS 손쉬운 사용 설정에서 DiskSage의 System Events 제어 권한이 필요합니다");
+ expect(source).toContain("권한이 없으면 요청만 실패하며 파일·클라우드 데이터는 변경되지 않습니다");
expect(source).toContain("local-volume-headroom-insufficient");
expect(source).toContain("local-volume-headroom-unverified");
expect(source).toContain("!nativeCopyHeadroomBlocked(candidate)");
From 14c9cb1fa540f9e8821fb96c058e7341887ea8f3 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:00:02 +0900
Subject: [PATCH 44/86] docs: record Finder permission guidance
---
.../adr/0006-redacted-icloud-health-evidence.md | 8 ++++++++
docs/product-technical-gap-baseline.md | 8 ++++----
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index d40fe9d2c..b0b6e6a07 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -215,6 +215,14 @@ media candidate remains eligible while the unsafe document candidate remains dia
partial. The focused suite passed 11/11 on Rust 1.97.1. No provider, Finder, source, or cloud
mutation rule changed.
+## Decision maintenance — 2026-08-24 17:59
+
+The Finder-copy cancellation control now tells the operator why macOS Accessibility/System Events
+permission is required to send the fixed Escape request, and explicitly states that a denied request
+does not mutate files or cloud data. The focused UI contract/privacy tests passed 6/6 and
+`npm run check` reported zero diagnostics. This is explanatory UX only; provider admission,
+attestation, and eviction remain fail-closed.
+
## Operational evidence update — 2026-08-24 16:21
The latest bounded read-only receipt still reports `evidence_complete=true` and
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index cc99091cb..9b7c80ac5 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:52 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:52 correction below supersede earlier
+**Snapshot:** 2026-08-24 17:59 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 17:59 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:52 +0900 current protected PR inventory
+## 2026-08-24 17:59 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `d5ffedb0872d07d933f7e0b95e42681c8e1c1174` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; upgrade-compatible stall journal and dated headroom-fixture repair |
+| #247 | `7b45ada3be4762724fd2c964b0dafe7f62dbeacb` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
From ce5a8cb3ac981d9c851f86248e474247bb80c8cc Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:09:25 +0900
Subject: [PATCH 45/86] docs: record current icon runtime PR head
---
docs/product-technical-gap-baseline.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 9b7c80ac5..71842f389 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 17:59 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 17:59 correction below supersede earlier
+**Snapshot:** 2026-08-24 18:08 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 18:08 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 17:59 +0900 current protected PR inventory
+## 2026-08-24 18:08 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -27,6 +27,7 @@ checks and approvals.
| #247 | `7b45ada3be4762724fd2c964b0dafe7f62dbeacb` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
+| #204 | `e62342c3daa45584f7d16b461e6be527a466bfd8` | yes | blocked | review required | DiskSage shell/icon identity; Node 20.19.0 and zlib ABI pinned after hosted runtime mismatch |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
| #212 | `75d728e403cf0b30511e149a7e650731f6472733` | no | blocked | review required | cloud operational CLI help |
| #206 | `2e7b845b7610a871ec5981d964bcab5cb99df41d` | no | clean | none | content-bound Homebrew execution; no qualifying approval |
From 776a87d7fbe11668ba3b85ea2bf57ade35334a2c Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:11:39 +0900
Subject: [PATCH 46/86] docs: refresh icon runtime PR head
---
docs/product-technical-gap-baseline.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 71842f389..3e8088ffc 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 18:08 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 18:08 correction below supersede earlier
+**Snapshot:** 2026-08-24 18:11 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 18:11 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -15,7 +15,7 @@ claimed from queued, stale, or bot-only status.
4. Regenerable caches are a separate reclaim domain. They are per-child, identity-bound, active-use checked, journaled, and moved to OS Trash; they are not uploaded as user data.
5. Deterministic Rust gates own safety. A local model may judge only the fixed maintenance command after dry-run evidence, calibration, and explicit human confirmation. No external LLM or OAuth service is a runtime prerequisite for the standalone product.
-## 2026-08-24 18:08 +0900 current protected PR inventory
+## 2026-08-24 18:11 +0900 current protected PR inventory
This is the current review queue captured from GitHub immediately before this snapshot. A commit
SHA is authoritative only for the PR row where it appears; a later push invalidates predecessor
@@ -27,7 +27,7 @@ checks and approvals.
| #247 | `7b45ada3be4762724fd2c964b0dafe7f62dbeacb` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
-| #204 | `e62342c3daa45584f7d16b461e6be527a466bfd8` | yes | blocked | review required | DiskSage shell/icon identity; Node 20.19.0 and zlib ABI pinned after hosted runtime mismatch |
+| #204 | `5bf86a9c593888fe5f08bff9f8dea74e5f1299ae` | yes | blocked | review required | DiskSage shell/icon identity; every Test/Release Node bootstrap and zlib ABI pinned after hosted runtime mismatch |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
| #212 | `75d728e403cf0b30511e149a7e650731f6472733` | no | blocked | review required | cloud operational CLI help |
| #206 | `2e7b845b7610a871ec5981d964bcab5cb99df41d` | no | clean | none | content-bound Homebrew execution; no qualifying approval |
From a38d7e803b5723f0c8306ac153f0efa01104d8c5 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:29:13 +0900
Subject: [PATCH 47/86] docs: refresh exact-head PR baseline
---
docs/product-technical-gap-baseline.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 3e8088ffc..6cba9c455 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 18:11 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 18:11 correction below supersede earlier
+**Snapshot:** 2026-08-24 18:28 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 18:28 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -24,11 +24,12 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `7b45ada3be4762724fd2c964b0dafe7f62dbeacb` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
+| #247 | `776a87d7fbe11668ba3b85ea2bf57ade35334a2c` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #204 | `5bf86a9c593888fe5f08bff9f8dea74e5f1299ae` | yes | blocked | review required | DiskSage shell/icon identity; every Test/Release Node bootstrap and zlib ABI pinned after hosted runtime mismatch |
| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
+| #225 | `ea6f82d914e4660319600acb614fccb4a701aec1` | yes | blocked | review required | cwd-relative organize targets fail closed while lineage metadata and Windows home-resolution contracts remain intact |
| #212 | `75d728e403cf0b30511e149a7e650731f6472733` | no | blocked | review required | cloud operational CLI help |
| #206 | `2e7b845b7610a871ec5981d964bcab5cb99df41d` | no | clean | none | content-bound Homebrew execution; no qualifying approval |
| #205 | `5c86668a6e503a174ff0b07151f67226b39547ff` | no | clean | none | Intel Homebrew target support; no qualifying approval |
From e4cfd1ce84148f490a94e0093e59a9ce9fb2f735 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:30:24 +0900
Subject: [PATCH 48/86] docs: record organize path safety amendment
---
.../adr/0001-cloud-offload-goal-state.md | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 8211ad5af..c9d0888b9 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -740,3 +740,18 @@ provider-API uploads remain a separate path. The source-volume snapshot remains
Pinned Rust tests, the destination-headroom contract, the full frontend suite (134 tests),
`svelte-check`, and frontend coverage all pass; mutation, attestation, and eviction authority are
unchanged and still fail closed.
+
+## Amendment: reconcile cwd-relative organize targets with current main (2026-08-24 18:28 +0900)
+
+PR #225 exact head `ea6f82d914e4660319600acb614fccb4a701aec1` now contains the current `main`
+lineage-aware organization contract and the original fail-closed target fix. `resolve_target_folder`
+expands only an exact `~` or leading `~/` against an absolute home path; process-cwd-relative,
+named-user tilde, parent/root traversal, and relative-home targets are rejected. Organization plans
+retain embedded-metadata-first production evidence (filename tokens such as `2026-04-28`/`251210`
+remain secondary), source size/mtime, and a lineage fingerprint; source drift is rechecked before
+execution.
+
+The exact-head local proof is 21 `organize::tests`, one Windows home-resolution contract test,
+`actionlint`, and `git diff --check`. Hosted checks were restarted for this head and remain
+authoritative; the draft PR has no qualifying approval. This amendment changes no Finder/provider,
+cloud, source-file, or eviction state.
From 2cfe934d7014e87f900dc0b0009a2176c82172bf Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:31:13 +0900
Subject: [PATCH 49/86] docs: bind baseline to ADR maintenance head
---
docs/product-technical-gap-baseline.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 6cba9c455..127e6b706 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,7 +1,7 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 18:28 +0900 (Asia/Seoul)
-**Repository heads at snapshot:** the dated inventory and 18:28 correction below supersede earlier
+**Snapshot:** 2026-08-24 18:30 +0900 (Asia/Seoul)
+**Repository heads at snapshot:** the dated inventory and 18:30 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
**Product boundary:** local-first macOS disk pressure relief with iCloud, OneDrive, and Google Drive destinations.
@@ -24,7 +24,7 @@ checks and approvals.
| PR | Exact head | Draft | Merge state | Review state | Current interpretation |
| --- | --- | --- | --- | --- | --- |
| #249 | `dc9ccf2a215061fba5bea2a23e8df3e84a0cd072` | yes | blocked | review required | Git worktree audit help; process tests use Cargo's shipped binary without nested temp builds |
-| #247 | `776a87d7fbe11668ba3b85ea2bf57ade35334a2c` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair and explicit Finder Accessibility permission guidance |
+| #247 | `e4cfd1ce84148f490a94e0093e59a9ce9fb2f735` | yes | blocked | review required | iCloud provider indexing plus live Finder/provider stall evidence; dated headroom-fixture repair, explicit Finder Accessibility permission guidance, and dynamic ADR maintenance |
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #204 | `5bf86a9c593888fe5f08bff9f8dea74e5f1299ae` | yes | blocked | review required | DiskSage shell/icon identity; every Test/Release Node bootstrap and zlib ABI pinned after hosted runtime mismatch |
From 4d05e084e34c27c16f771f5063960128ec43ea2b Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 18:53:19 +0900
Subject: [PATCH 50/86] docs: record current CI and provider evidence
---
.../adr/0001-cloud-offload-goal-state.md | 10 ++++++++
docs/product-technical-gap-baseline.md | 24 ++++++++++++++++---
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index c9d0888b9..e9dde086c 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -755,3 +755,13 @@ The exact-head local proof is 21 `organize::tests`, one Windows home-resolution
`actionlint`, and `git diff --check`. Hosted checks were restarted for this head and remain
authoritative; the draft PR has no qualifying approval. This amendment changes no Finder/provider,
cloud, source-file, or eviction state.
+
+## Amendment: make platform fixtures and active-use CI evidence explicit (2026-08-24 18:51 +0900)
+
+PR #225 exact head `3715a5ada760072d3675026fe7f264b4ee47964f` keeps the resolver fail-closed on
+Windows by changing only the regression fixtures from POSIX `/home/u` to the existing platform
+absolute-home helper. PR #227 exact head `753352a1d0cd7e297bb656d5edf9339235a628a3` installs `lsof`
+in the Ubuntu test image so active-use evidence remains complete in CI; production behavior still
+fails closed when the probe is unavailable. Local proofs are 21/21 organize tests and 735/735 Rust
+tests with one ignored live-provider test. These CI/fixture repairs grant no copy, cloud-write,
+attestation, source-eviction, Finder-cancel, or provider-restart authority.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 127e6b706..563eee090 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1,6 +1,6 @@
# DiskSage product and technical gap baseline
-**Snapshot:** 2026-08-24 18:30 +0900 (Asia/Seoul)
+**Snapshot:** 2026-08-24 18:51 +0900 (Asia/Seoul)
**Repository heads at snapshot:** the dated inventory and 18:30 correction below supersede earlier
historical captures; hosted checks and protected review remain authoritative, and no merge is
claimed from queued, stale, or bot-only status.
@@ -28,8 +28,8 @@ checks and approvals.
| #246 | `1972614ee5488cca34deeb3bd999d369c61b3de1` | no | blocked | review required | Storybook/accessibility contract; iCloud stall clock test slice is 7/7 |
| #244 | `13caeb04333e50e57c8a51a11b64aeb131c080b2` | no | blocked | review required | Rust 1.97.1 compiler baseline |
| #204 | `5bf86a9c593888fe5f08bff9f8dea74e5f1299ae` | yes | blocked | review required | DiskSage shell/icon identity; every Test/Release Node bootstrap and zlib ABI pinned after hosted runtime mismatch |
-| #227 | `5ad11975ad1229e700dc042fdfd0482f21a2f45c` | no | blocked | review required | symlink-root audit hardening |
-| #225 | `ea6f82d914e4660319600acb614fccb4a701aec1` | yes | blocked | review required | cwd-relative organize targets fail closed while lineage metadata and Windows home-resolution contracts remain intact |
+| #227 | `753352a1d0cd7e297bb656d5edf9339235a628a3` | yes | blocked | review required | symlink-root audit hardening; Ubuntu active-use tests now install `lsof` |
+| #225 | `3715a5ada760072d3675026fe7f264b4ee47964f` | yes | blocked | review required | cwd-relative organize targets fail closed; Windows regressions use platform-absolute homes |
| #212 | `75d728e403cf0b30511e149a7e650731f6472733` | no | blocked | review required | cloud operational CLI help |
| #206 | `2e7b845b7610a871ec5981d964bcab5cb99df41d` | no | clean | none | content-bound Homebrew execution; no qualifying approval |
| #205 | `5c86668a6e503a174ff0b07151f67226b39547ff` | no | clean | none | Intel Homebrew target support; no qualifying approval |
@@ -192,6 +192,24 @@ is processed exact-head-first: review, repair, recheck, then normal protected me
At each scheduled or operator loop, update this file only with new dated evidence: current head, open-PR/check state, provider receipt state, disk headroom, and the smallest acceptance proof completed. Do not convert an incomplete provider probe, filename date, model answer, or GitHub review comment into a transfer or deletion authority.
+## 2026-08-24 18:51 +0900 exact-head and host delta
+
+- PR #225 is at `3715a5ada760072d3675026fe7f264b4ee47964f`; its Windows failure was reproduced from
+ the hosted log: two tests supplied POSIX `/home/u`, which the fail-closed Windows resolver correctly
+ rejects. The existing `platform_home()` fixture now supplies a Windows absolute path; pinned Rust
+ organize tests pass 21/21, with `rustfmt --check` and `git diff --check` passing.
+- PR #227 is at `753352a1d0cd7e297bb656d5edf9339235a628a3`; its Ubuntu failure was caused by the
+ runner missing `lsof`, which the fail-closed active-use probe requires. The existing system-dependency
+ step now installs `lsof`; the local full Rust suite passes 735/735 with one ignored live-provider test.
+- A fresh read-only host observation measured 16 GiB available on `/` (926 GiB total, 43% used), while
+ `brctl status` still reports iCloud `needs-sync` and repeated `pending-scan` entries roughly 1.37 hours
+ old. Finder, `fileproviderd`, and `bird` are running; no DiskSage process was present. This supports a
+ provider reconciliation/indexing stall, not local disk exhaustion or a proven DiskSage lock.
+- The Finder `real_datasets` copy remains unmaterialized in the bounded provider evidence. No Finder
+ cancellation, provider restart, CloudDocs write, cloud mutation, source mutation, attestation, or
+ eviction was performed; `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+ `mutation_performed=false` remain the only safe state until fresh per-item evidence exists.
+
## 2026-08-21 23:30 +0900 live iCloud Finder-preparation receipt
- The exact-head `disksage-icloud-sync-health` binary completed a bounded, read-only iCloud
From cf6c4f77383eeaa8cf1edfa8a6d1f1c4a873e119 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 19:09:26 +0900
Subject: [PATCH 51/86] docs: record current iCloud provider stall evidence
---
.../adr/0001-cloud-offload-goal-state.md | 11 +++++++++++
docs/product-technical-gap-baseline.md | 12 ++++++++++++
2 files changed, 23 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index e9dde086c..eee81b18a 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -765,3 +765,14 @@ in the Ubuntu test image so active-use evidence remains complete in CI; producti
fails closed when the probe is unavailable. Local proofs are 21/21 organize tests and 735/735 Rust
tests with one ignored live-provider test. These CI/fixture repairs grant no copy, cloud-write,
attestation, source-eviction, Finder-cancel, or provider-restart authority.
+
+## Amendment: re-confirm the live Finder preparation blocker without mutation (2026-08-24 19:04 +0900)
+
+A fresh read-only `/usr/bin/brctl status` still reports iCloud `client:needs-sync` with
+`needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`, 1,740 `pending-scan` entries, and
+343 `pending-sync-up` entries. Finder, `fileproviderd`, and `bird` are present, while no DiskSage
+process is running. The root volume has about 12 GiB available, so global fullness is not proven;
+the screenshot's seven-item copy size and destination receipt remain unknown. This is aggregate
+provider reconciliation evidence only: `provider-sync-incomplete`, copy/attestation, cloud-write,
+and source-eviction authority remain fail-closed, and no Finder, provider, source, or cloud
+mutation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 563eee090..8c8c42416 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1331,3 +1331,15 @@ checks are not reused:
- Exact-head review evidence remains current: #249 is now `6b95c59` after centralizing the CLI's
reference validation in the library; #246 is `1972614`; #227 is `5ad1197`. Hosted checks and
protected independent approvals remain the only merge gates.
+
+## 2026-08-24 19:04 +0900 live iCloud queue still explains the copy-preparation stall
+
+- A fresh read-only `/usr/bin/brctl status` still reports the iCloud client as `needs-sync` with
+ `needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`. The bounded dump contains 1,740
+ `pending-scan` entries and 343 `pending-sync-up` entries; the queue remains provider-global and
+ does not identify the seven Finder items.
+- The host has about 12 GiB available on `/`, and the read-only process inventory contains Finder,
+ `fileproviderd`, and `bird` but no DiskSage process. This is consistent with provider
+ reconciliation/indexing pressure; it is not proof that DiskSage owns a Finder lock, nor proof
+ that the cloud write completed. Per-item copy, attestation, and local eviction remain
+ fail-closed; no Finder, provider, source, or cloud mutation occurred.
From 7f438488377dc35dae82ffd8fcd8f59e73fa0825 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 19:10:57 +0900
Subject: [PATCH 52/86] docs: record exact iCloud health receipt
---
.../adr/0001-cloud-offload-goal-state.md | 10 ++++++++++
docs/product-technical-gap-baseline.md | 12 ++++++++++++
2 files changed, 22 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index eee81b18a..f16c504ab 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -776,3 +776,13 @@ the screenshot's seven-item copy size and destination receipt remain unknown. Th
provider reconciliation evidence only: `provider-sync-incomplete`, copy/attestation, cloud-write,
and source-eviction authority remain fail-closed, and no Finder, provider, source, or cloud
mutation was performed.
+
+## Amendment: exact-head health receipt keeps new copy admission blocked (2026-08-24 19:10 +0900)
+
+The exact-head `disksage-icloud-sync-health` probe completed as a read-only report with
+`evidence_complete=true`, `new_copy_admission_state=blocked`, and
+`pending_indexable_count=151283`; one upload is active at 95.24% and one download is active.
+The report records 343 uploads blocked on sync-up and retains
+`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
+The aggregate receipt cannot attest the seven Finder items or a remote cloud write, so copy,
+cloud-write, and source-eviction authority remain fail-closed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 8c8c42416..957d45c01 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1343,3 +1343,15 @@ checks are not reused:
reconciliation/indexing pressure; it is not proof that DiskSage owns a Finder lock, nor proof
that the cloud write completed. Per-item copy, attestation, and local eviction remain
fail-closed; no Finder, provider, source, or cloud mutation occurred.
+
+## 2026-08-24 19:10 +0900 exact-head iCloud health receipt
+
+- The exact-head `disksage-icloud-sync-health` probe completed read-only with
+ `evidence_complete=true`, `new_copy_admission_state=blocked`, and
+ `pending_indexable_count=151283`; one upload is active at 95.24% and one download is active.
+ The report retains `provider_sync_attested=false`, `local_eviction_authorized=false`, and
+ `mutation_performed=false`.
+- The blockers include native sync-up pending, 343 uploads blocked on sync-up, File Provider
+ indexing/disk-import/transfer activity, and filename/root exclusions. This is still aggregate
+ provider evidence rather than a per-item receipt for the seven Finder entries; no cloud write or
+ source eviction is authorized.
From a1a0fa2dc1279da6da81bb34f2053c185e296b0a Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 19:29:11 +0900
Subject: [PATCH 53/86] docs: record latest git audit repair
---
.../architecture/adr/0001-cloud-offload-goal-state.md | 9 +++++++++
docs/product-technical-gap-baseline.md | 11 +++++++++++
2 files changed, 20 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index f16c504ab..77d34aa91 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -786,3 +786,12 @@ The report records 343 uploads blocked on sync-up and retains
`provider_sync_attested=false`, `local_eviction_authorized=false`, and `mutation_performed=false`.
The aggregate receipt cannot attest the seven Finder items or a remote cloud write, so copy,
cloud-write, and source-eviction authority remain fail-closed.
+
+## Amendment: record the exact-head Git worktree audit compile repair (2026-08-24 19:28 +0900)
+
+DiskSage #249 exact head `c8ca669262f913de5719ebda377132f1135c06c8` repairs the hosted
+all-features `E0425` by making the library-owned `MAX_REFERENCE_BYTES` bound available to its CLI
+unit tests without duplicating the validation contract. Pinned Rust 1.97.1 proofs passed 7/7 CLI
+tests and 10/10 black-box Git-worktree tests. The audit remains read-only and path-redacted; no
+worktree removal, Finder/provider operation, cloud write, or source eviction is authorized by this
+repair.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 957d45c01..b11995a47 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1355,3 +1355,14 @@ checks are not reused:
indexing/disk-import/transfer activity, and filename/root exclusions. This is still aggregate
provider evidence rather than a per-item receipt for the seven Finder entries; no cloud write or
source eviction is authorized.
+
+## 2026-08-24 19:28 +0900 exact-head Git worktree audit repair
+
+- DiskSage #249 advanced to exact head `c8ca669262f913de5719ebda377132f1135c06c8`. The hosted
+ all-features compile failure was traced to CLI tests referencing the library's private
+ `MAX_REFERENCE_BYTES` bound; the bound is now exported once by the library and imported only by
+ the CLI test module. Pinned Rust 1.97.1 local proofs passed 7/7 CLI tests and 10/10 black-box
+ Git-worktree tests.
+- The audit remains read-only, path-redacted, create-once for private evidence, and grants no
+ worktree-removal authority. The new exact head has no failed checks yet; hosted checks and
+ protected approvals remain authoritative. No user, Finder, provider, or cloud data was changed.
From 36a56951214afc72a1ab8d953d03f08d84b10d25 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 19:40:05 +0900
Subject: [PATCH 54/86] docs: record post-recovery iCloud blocker recheck
---
.../architecture/adr/0001-cloud-offload-goal-state.md | 11 +++++++++++
docs/product-technical-gap-baseline.md | 10 ++++++++++
2 files changed, 21 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 77d34aa91..967dc7aef 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -795,3 +795,14 @@ unit tests without duplicating the validation contract. Pinned Rust 1.97.1 proof
tests and 10/10 black-box Git-worktree tests. The audit remains read-only and path-redacted; no
worktree removal, Finder/provider operation, cloud write, or source eviction is authorized by this
repair.
+
+## Amendment: recheck the Finder preparation blocker after scheduler recovery (2026-08-24 19:38 +0900)
+
+A fresh read-only `/usr/bin/brctl status` still reports the iCloud client as `needs-sync` with
+`needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`; the last native sync remains
+`2026-08-21 20:20:10.166`. Finder, `fileproviderd`, and `bird` are running, while no DiskSage
+process is present. The root volume has about 12 GiB available. This is provider-global
+reconciliation evidence consistent with the multi-hour Finder `real_datasets` preparation stall,
+not a per-item receipt and not proof of a DiskSage lock or cloud write. The existing
+`provider-sync-incomplete` admission, copy/attestation, and source-eviction gates therefore stay
+fail-closed; no Finder, provider, source, or cloud mutation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index b11995a47..e7c18b5d8 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1366,3 +1366,13 @@ checks are not reused:
- The audit remains read-only, path-redacted, create-once for private evidence, and grants no
worktree-removal authority. The new exact head has no failed checks yet; hosted checks and
protected approvals remain authoritative. No user, Finder, provider, or cloud data was changed.
+
+## 2026-08-24 19:38 +0900 post-recovery iCloud recheck
+
+- A fresh read-only `/usr/bin/brctl status` still reports `client:needs-sync` and
+ `needs-sync-up|in-sync-down|prefer-sync-down|oob-sync-ack`; native last-sync remains
+ `2026-08-21 20:20:10.166`.
+- Finder, `fileproviderd`, and `bird` are present with no DiskSage process; `/` has about 12 GiB
+ available. This remains aggregate provider reconciliation evidence for the Finder
+ `real_datasets` preparation stall, not proof of a DiskSage lock or a completed cloud write.
+ Copy admission, attestation, and source eviction remain fail-closed; no mutation was performed.
From f295c80d5f405532eb9754ca781e6d48d34588c5 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Mon, 24 Aug 2026 19:52:00 +0900
Subject: [PATCH 55/86] feat(ui): export path-free cloud lineage graph
---
.../adr/0001-cloud-offload-goal-state.md | 11 ++
docs/product-technical-gap-baseline.md | 11 ++
src/lib/CloudArchive.svelte | 20 +++
src/lib/cloudLineageExport.test.ts | 79 ++++++++++++
src/lib/cloudLineageExport.ts | 115 ++++++++++++++++++
5 files changed, 236 insertions(+)
create mode 100644 src/lib/cloudLineageExport.test.ts
create mode 100644 src/lib/cloudLineageExport.ts
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 967dc7aef..c1ba781bb 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -806,3 +806,14 @@ reconciliation evidence consistent with the multi-hour Finder `real_datasets` pr
not a per-item receipt and not proof of a DiskSage lock or cloud write. The existing
`provider-sync-incomplete` admission, copy/attestation, and source-eviction gates therefore stay
fail-closed; no Finder, provider, source, or cloud mutation was performed.
+
+## Amendment: expose a path-free lineage graph for catalog handoff (2026-08-24 19:52 +0900)
+
+The CloudArchive receipt view now offers a JSON export only when a verified receipt contains the
+modern lineage fingerprint. The export is a bounded client-side graph with stable content,
+metadata, archive, provider, receipt, Goal, and optional eviction node identifiers; it carries
+production-time source/confidence, provider sync state, and sorted blockers, while explicitly
+setting `local_paths_included=false`. It never fabricates a provider item, attestation, Goal
+completion, or eviction relation: legacy receipts without lineage are not exportable, and an
+eviction edge appears only after the real eviction output exists. This is an export/view action
+only; it performs no provider, source, cloud, ADR, or Goal mutation.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index e7c18b5d8..c2724f9b2 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1376,3 +1376,14 @@ checks are not reused:
available. This remains aggregate provider reconciliation evidence for the Finder
`real_datasets` preparation stall, not proof of a DiskSage lock or a completed cloud write.
Copy admission, attestation, and source eviction remain fail-closed; no mutation was performed.
+
+## 2026-08-24 19:52 +0900 path-free lineage handoff proof
+
+- The buyer-visible P1 lineage gap now has a minimal export path: CloudArchive can download a
+ `disksage.cloud-lineage` JSON graph from a verified modern receipt, connecting source,
+ metadata, archive, provider, receipt, Goal, and (only after actual eviction) eviction nodes.
+- The export includes stable content IDs, production metadata source/confidence, provider sync
+ state, and blockers, but no raw local or destination paths. Legacy receipts without a lineage
+ fingerprint fail closed. Frontend `npm run check`, all 137 frontend tests, and 100% V8
+ statements/branches/functions/lines pass; the export itself is read-only and does not change
+ provider, source, cloud, ADR, or Goal state.
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index c4889ea3f..4b8be7e61 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -21,6 +21,7 @@
} from "./cloudArchiveHealthTiming";
import { fmtBytes } from "./fmt";
import IcloudLocalEviction from "./IcloudLocalEviction.svelte";
+ import { buildCloudLineageExport } from "./cloudLineageExport";
const RECONCILIATION_INTERVAL_MS = 60_000;
// fileproviderctl can spend tens of seconds inside the system provider database while iCloud is
@@ -469,6 +470,19 @@
}
}
+ function downloadLineageExport() {
+ if (!copied) return;
+ const graph = buildCloudLineageExport(copied, attestation, eviction);
+ if (!graph) return;
+ const blob = new Blob([JSON.stringify(graph, null, 2)], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const anchor = document.createElement("a");
+ anchor.href = url;
+ anchor.download = `disksage-lineage-${graph.content_id.slice(0, 12)}.json`;
+ anchor.click();
+ URL.revokeObjectURL(url);
+ }
+
async function reconcileCloudReceipts() {
reconciling = true;
reconciliationError = "";
@@ -1312,6 +1326,12 @@
이 작업은 Finder에 Escape 키를 보내므로 macOS 손쉬운 사용 설정에서 DiskSage의 System Events 제어 권한이 필요합니다. 권한이 없으면 요청만 실패하며 파일·클라우드 데이터는 변경되지 않습니다.
@@ -1012,6 +1016,12 @@
File Provider 상태가 정상으로 관찰된 뒤 DiskSage에서 새 계획을 다시 실행해야 합니다.
{/if}
+ {#if (icloudHealth.native_status?.pending_scan_count ?? 0) > 0}
+
+ macOS iCloud native 상태에 pending-scan {icloudHealth.native_status?.pending_scan_count}개가 남아 있습니다.
+ Finder의 “복사 준비 중”은 완료 영수증이 아니므로 scan 대기가 해소될 때까지 새 복사·attestation·원본 정리를 진행하지 않습니다.
+
- macOS iCloud native 상태에 pending-scan {icloudHealth.native_status?.pending_scan_count}개가 남아 있습니다.
+ macOS iCloud native 상태에서 pending-scan 항목이 관찰되었습니다(경계 내 {icloudHealth.native_status?.pending_scan_count}개).
Finder의 “복사 준비 중”은 완료 영수증이 아니므로 scan 대기가 해소될 때까지 새 복사·attestation·원본 정리를 진행하지 않습니다.
{/if}
From 9fdf2922da2939d96d3c2393539f2b2d42009929 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 09:11:41 +0900
Subject: [PATCH 64/86] feat(icloud): project health blockers into goal state
---
.../adr/0001-cloud-offload-goal-state.md | 11 ++
.../0006-redacted-icloud-health-evidence.md | 8 +
docs/product-technical-gap-baseline.md | 10 +-
src-tauri/src/commands.rs | 167 ++++++++++++++++++
4 files changed, 195 insertions(+), 1 deletion(-)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index f0e2e77fe..023879d80 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -854,3 +854,14 @@ provider observation, not a per-item cloud receipt: a Finder “복사 준비
unverified, `provider-sync-incomplete`, and blocked for copy, attestation, cloud write, and source
eviction until the scan backlog is gone and item-level provider evidence is present. The Naruon
readiness export and CloudArchive UI carry the same blocker and show the bounded next action.
+
+## Amendment: persist native health blockers in runtime projections (2026-08-25 00:00 +0900)
+
+When an iCloud admission probe is persisted, DiskSage now selects the native pending-scan blocker
+when present and applies it to every bounded, valid iCloud receipt projection. The replaceable Goal
+is written as `blocked` with `provider-sync-state-complete=false` and
+`explicit-eviction-permit=false`; the paired ADR records `provider-state-blocked:`. This
+is a projection update only: immutable receipts remain authoritative, and no provider, Finder,
+source, cloud, attestation, or eviction mutation is performed. A missing, malformed, oversized,
+or incomplete receipt set emits a stable projection warning instead of claiming that all Goals were
+updated.
diff --git a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
index 5dc67817d..30dc86043 100644
--- a/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
+++ b/docs/architecture/adr/0006-redacted-icloud-health-evidence.md
@@ -328,3 +328,11 @@ The native status contract now retains only a bounded `pending_scan_count` deriv
`icloud-native-status-pending-scan`. It never persists the marker's path or item identifier. The
same blocker is validated in Naruon readiness and displayed beside the Finder cancellation
guidance; it does not authorize cancellation, cloud writes, attestation, or source eviction.
+
+## Runtime projection update — 2026-08-25 00:00
+
+The iCloud health persistence path now propagates the selected bounded blocker to existing iCloud
+receipt-linked Goal/ADR projections. Goal status and completion gates therefore reflect the current
+provider-sync hold after restart or a manual health inspection, while receipt/evidence authority is
+unchanged. Projection directory, receipt contents, and provider identifiers are not included in
+the emitted notices.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index f3727af6c..6b99fea3e 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1421,4 +1421,12 @@ checks are not reused:
`icloud-native-status-pending-scan`, propagates it through Naruon readiness, and shows it in the
CloudArchive UI. This keeps the Finder `real_datasets` “복사 준비 중” state explicitly blocked
without treating the screenshot as an upload receipt; no Finder, provider, source, or cloud
- mutation is performed.
+mutation is performed.
+
+## 2026-08-25 00:00 +0900 dynamic Goal/ADR propagation
+
+- The previous native pending-scan implementation stopped at health/readiness/UI and did not update
+ receipt-linked runtime projections. The iCloud health persistence path now applies the selected
+ blocker to bounded valid iCloud receipt projections: Goal becomes `blocked` and revokes provider
+ completion and eviction gates; the paired ADR records the provider-state blocker. Projection
+ failures remain explicit and path-free, and no provider/Finder/source/cloud mutation occurs.
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index a499356d8..c2d1d662c 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -1288,9 +1288,117 @@ fn persist_icloud_health_evidence(
}
report.admission_blocked_since_ms =
icloud_sync_health::admission_blocked_since_ms(&app_data_dir, report);
+ let provider_blocker = report
+ .new_copy_admission_blockers
+ .iter()
+ .find(|blocker| blocker.as_str() == "icloud-native-status-pending-scan")
+ .or_else(|| report.new_copy_admission_blockers.first());
+ if let Some(provider_blocker) = provider_blocker {
+ report.notices.extend(update_icloud_goal_projections(
+ &app_data_dir.join("cloud-receipts"),
+ &app_data_dir.join("cloud-adr"),
+ &app_data_dir.join("cloud-goals"),
+ cloud::system_now_ms(),
+ provider_blocker,
+ ));
+ }
true
}
+#[cfg(not(coverage))]
+fn apply_icloud_health_blocker_to_projection(
+ receipt: &cloud_transfer::CloudCopyReceipt,
+ adr_dir: &Path,
+ goal_dir: &Path,
+ observed_at_ms: u64,
+ provider_blocker: &str,
+) -> cloud_adr::ProjectionWriteOutcome {
+ cloud_adr::ensure_initial_projection_pair_with_provider_state_outcome(
+ receipt,
+ adr_dir,
+ goal_dir,
+ observed_at_ms,
+ provider_blocker,
+ )
+}
+
+#[cfg(not(coverage))]
+fn update_icloud_goal_projections(
+ receipt_dir: &Path,
+ adr_dir: &Path,
+ goal_dir: &Path,
+ observed_at_ms: u64,
+ provider_blocker: &str,
+) -> Vec {
+ match std::fs::symlink_metadata(receipt_dir) {
+ Ok(metadata) if metadata.is_dir() && !metadata.file_type().is_symlink() => {}
+ Ok(_) => return vec!["dynamic-goal-projection-update-incomplete".into()],
+ Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Vec::new(),
+ Err(_) => return vec!["dynamic-goal-projection-update-incomplete".into()],
+ }
+ let mut paths = match std::fs::read_dir(receipt_dir) {
+ Ok(entries) => entries
+ .filter_map(Result::ok)
+ .map(|entry| entry.path())
+ .collect::>(),
+ Err(_) => return vec!["dynamic-goal-projection-update-incomplete".into()],
+ };
+ paths.sort();
+ if paths.len() > MAX_CLOUD_RECEIPT_RECONCILIATION_ENTRIES {
+ return vec!["dynamic-goal-projection-update-incomplete".into()];
+ }
+ let started = Instant::now();
+ let mut updated = 0usize;
+ let mut incomplete = false;
+ for (index, path) in paths.iter().enumerate() {
+ if index >= MAX_CLOUD_RECEIPTS_PER_RECONCILIATION
+ || started.elapsed() >= CLOUD_RECONCILIATION_MAX_DURATION
+ {
+ incomplete = true;
+ break;
+ }
+ let Ok(file_metadata) = std::fs::symlink_metadata(path) else {
+ incomplete = true;
+ continue;
+ };
+ if file_metadata.file_type().is_symlink()
+ || !file_metadata.is_file()
+ || path.extension().and_then(|value| value.to_str()) != Some("json")
+ {
+ continue;
+ }
+ let receipt = match cloud_transfer::read_immutable_receipt(path) {
+ Ok(receipt) => receipt,
+ Err(_) => {
+ incomplete = true;
+ continue;
+ }
+ };
+ if receipt.provider != cloud::CloudProvider::Icloud {
+ continue;
+ }
+ let outcome = apply_icloud_health_blocker_to_projection(
+ &receipt,
+ adr_dir,
+ goal_dir,
+ observed_at_ms,
+ provider_blocker,
+ );
+ if outcome.wrote {
+ updated = updated.saturating_add(1);
+ }
+ incomplete |= !outcome.warnings.is_empty();
+ }
+ let mut notices = Vec::new();
+ if updated > 0 {
+ notices.push("dynamic-goal-projection-updated".into());
+ }
+ if incomplete {
+ notices.push("dynamic-goal-projection-update-incomplete".into());
+ }
+ notices
+}
+
#[cfg(not(coverage))]
fn persist_provider_global_sync_evidence(
app: &AppHandle,
@@ -3184,6 +3292,65 @@ mod tests {
);
}
+ #[cfg(not(coverage))]
+ #[test]
+ fn icloud_health_blocker_updates_dynamic_goal_and_adr_projections() {
+ let temporary = tempfile::tempdir().unwrap();
+ let receipt = cloud_transfer::CloudCopyReceipt {
+ version: cloud_transfer::RECEIPT_VERSION,
+ receipt_id: "a".repeat(64),
+ candidate_fingerprint: "b".repeat(64),
+ provider: cloud::CloudProvider::Icloud,
+ source: "/source/file.bin".into(),
+ destination: "/cloud/file.bin".into(),
+ bytes: 1,
+ blake3: "c".repeat(64),
+ sha256: "d".repeat(64),
+ quick_xor_base64: String::new(),
+ source_modified_ms: 1,
+ copied_at_ms: 2,
+ copy_verified: true,
+ provider_sync_confirmed: false,
+ lineage_fingerprint: None,
+ lineage: None,
+ };
+ let adr_dir = temporary.path().join("adr");
+ let goal_dir = temporary.path().join("goals");
+ let initial = cloud_adr::write_projection_pair(
+ &adr_dir,
+ &cloud_adr::initial_adr_snapshot(&receipt, 2),
+ &goal_dir,
+ &cloud_adr::initial_goal_snapshot(&receipt, 2),
+ );
+ assert!(initial.0.is_some() && initial.1.is_some());
+
+ let outcome = apply_icloud_health_blocker_to_projection(
+ &receipt,
+ &adr_dir,
+ &goal_dir,
+ 3,
+ "icloud-native-status-pending-scan",
+ );
+ assert!(outcome.wrote);
+ assert!(outcome.warnings.is_empty());
+
+ let goal: cloud_adr::CloudOffloadGoalSnapshot = serde_json::from_slice(
+ &std::fs::read(goal_dir.join(format!("{}-latest.json", receipt.receipt_id))).unwrap(),
+ )
+ .unwrap();
+ assert_eq!(goal.status, "blocked");
+ assert!(!goal.completion_gates["provider-sync-state-complete"]);
+ assert!(!goal.completion_gates["explicit-eviction-permit"]);
+ let adr: cloud_adr::CloudOffloadAdrSnapshot = serde_json::from_slice(
+ &std::fs::read(adr_dir.join(format!("{}-latest.json", receipt.receipt_id))).unwrap(),
+ )
+ .unwrap();
+ assert!(adr
+ .consequences
+ .iter()
+ .any(|value| value == "provider-state-blocked:icloud-native-status-pending-scan"));
+ }
+
#[cfg(not(coverage))]
#[test]
fn reconciliation_without_receipts_is_read_only() {
From 72e8c29928666ba084157216dd58d3429f0b3057 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 09:24:36 +0900
Subject: [PATCH 65/86] docs: record current Google Drive provider stall
---
.../adr/0001-cloud-offload-goal-state.md | 22 +++++++++++++++++++
docs/product-technical-gap-baseline.md | 17 ++++++++++++++
2 files changed, 39 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 023879d80..32db5e5e9 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -865,3 +865,25 @@ is a projection update only: immutable receipts remain authoritative, and no pro
source, cloud, attestation, or eviction mutation is performed. A missing, malformed, oversized,
or incomplete receipt set emits a stable projection warning instead of claiming that all Goals were
updated.
+
+## Amendment: current Google Drive preparation diagnosis (2026-08-25 09:23 +0900)
+
+A fresh bounded read-only `fileproviderctl dump com.google.drivefs.fpext -l` identified the provider
+shown behind the `real_datasets` Finder dialog as temporarily disconnected. The dump reported File
+Provider `-1004` server-unreachable failures for the root metadata fetch, active upload and download
+progress markers, a 2,000-entry reconciliation section, and a provider error generation above zero.
+The local Google Drive mount exposed no materialized `real_datasets` destination at observation time;
+the 7.2 GiB source remained local and unchanged. The root volume had about 2.1 GiB available, so a
+retry that might stage the source locally is unsafe even though this particular dump did not emit an
+explicit disk-full marker.
+
+System Events did not enumerate an active Finder copy-progress window during the later read-only
+check. That absence is not evidence of a completed copy: no destination receipt or remote content
+proof exists. The default route through `utun4` is recorded as network context only, not as a proven
+root cause. DiskSage therefore keeps `provider-global-sync-temporarily-disconnected`,
+`provider-global-sync-server-unreachable`, `provider-global-sync-transfer-active`, and
+`provider-global-sync-reconciliation-pending` fail-closed for copy, attestation, and source
+eviction. No Finder cancellation, provider restart, cloud write, source mutation, or eviction was
+performed. The observation was made against DiskSage PR #247 exact head
+`9fdf2922da2939d96d3c2393539f2b2d42009929`; the PR remains draft, review-required, and blocked while
+hosted checks are pending.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 6b99fea3e..5b4403e5f 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1430,3 +1430,20 @@ mutation is performed.
blocker to bounded valid iCloud receipt projections: Goal becomes `blocked` and revokes provider
completion and eviction gates; the paired ADR records the provider-state blocker. Projection
failures remain explicit and path-free, and no provider/Finder/source/cloud mutation occurs.
+
+## 2026-08-25 09:23 +0900 current Google Drive Finder-preparation diagnosis
+
+- The screenshot's destination is Google Drive, not iCloud. A bounded read-only
+ `fileproviderctl dump com.google.drivefs.fpext -l` reported `temporarily disconnected`, File
+ Provider `-1004` server-unreachable root metadata failures, active upload and download progress,
+ and a 2,000-entry reconciliation backlog. This is the provider-global explanation for Finder
+ remaining at “복사 준비 중”; it is not a per-item cloud receipt or proof of a completed copy.
+- The 7.2 GiB `real_datasets` source remained local and unchanged, no destination folder or receipt
+ was observed, and the root volume had about 2.1 GiB free. DiskSage retains the existing stable
+ provider-global blockers and refuses copy, attestation, and source eviction until a fresh quiet
+ provider observation. No Finder, provider, source, or cloud mutation was performed.
+- The exact DiskSage PR #247 head is
+ `9fdf2922da2939d96d3c2393539f2b2d42009929`; its hosted checks are still pending and the protected
+ PR remains draft/blocked/review-required. The host's `utun4` default route is recorded only as
+ context, not as a proven root cause. The filename dates `2026-04-28` and `251210` remain
+ auxiliary production-time evidence; embedded metadata and context retain precedence.
From 87c9089bcd4af49f8f8751c54ebcc45b519d1f0c Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 09:38:19 +0900
Subject: [PATCH 66/86] fix(cloud): project provider sync blockers into goal
---
src-tauri/src/commands.rs | 78 ++++++++++++++++++++++++++++++++++++---
1 file changed, 72 insertions(+), 6 deletions(-)
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index c2d1d662c..7719d96d7 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -1294,11 +1294,12 @@ fn persist_icloud_health_evidence(
.find(|blocker| blocker.as_str() == "icloud-native-status-pending-scan")
.or_else(|| report.new_copy_admission_blockers.first());
if let Some(provider_blocker) = provider_blocker {
- report.notices.extend(update_icloud_goal_projections(
+ report.notices.extend(update_provider_goal_projections(
&app_data_dir.join("cloud-receipts"),
&app_data_dir.join("cloud-adr"),
&app_data_dir.join("cloud-goals"),
cloud::system_now_ms(),
+ cloud::CloudProvider::Icloud,
provider_blocker,
));
}
@@ -1306,7 +1307,7 @@ fn persist_icloud_health_evidence(
}
#[cfg(not(coverage))]
-fn apply_icloud_health_blocker_to_projection(
+fn apply_provider_blocker_to_projection(
receipt: &cloud_transfer::CloudCopyReceipt,
adr_dir: &Path,
goal_dir: &Path,
@@ -1323,11 +1324,12 @@ fn apply_icloud_health_blocker_to_projection(
}
#[cfg(not(coverage))]
-fn update_icloud_goal_projections(
+fn update_provider_goal_projections(
receipt_dir: &Path,
adr_dir: &Path,
goal_dir: &Path,
observed_at_ms: u64,
+ provider: cloud::CloudProvider,
provider_blocker: &str,
) -> Vec {
match std::fs::symlink_metadata(receipt_dir) {
@@ -1374,10 +1376,10 @@ fn update_icloud_goal_projections(
continue;
}
};
- if receipt.provider != cloud::CloudProvider::Icloud {
+ if receipt.provider != provider {
continue;
}
- let outcome = apply_icloud_health_blocker_to_projection(
+ let outcome = apply_provider_blocker_to_projection(
&receipt,
adr_dir,
goal_dir,
@@ -1412,6 +1414,20 @@ fn persist_provider_global_sync_evidence(
}
report.admission_blocked_since_ms =
provider_global_sync::provider_global_sync_blocked_since_ms(&app_data_dir, report);
+ let provider_blocker = report.blockers.first().cloned().or_else(|| {
+ (report.state != provider_global_sync::ProviderGlobalSyncState::Clear)
+ .then(|| format!("provider-global-sync-{}", report.state.as_str()))
+ });
+ if let Some(provider_blocker) = provider_blocker.as_deref() {
+ report.notices.extend(update_provider_goal_projections(
+ &app_data_dir.join("cloud-receipts"),
+ &app_data_dir.join("cloud-adr"),
+ &app_data_dir.join("cloud-goals"),
+ report.observed_at_ms,
+ report.provider,
+ provider_blocker,
+ ));
+ }
true
}
@@ -3324,7 +3340,7 @@ mod tests {
);
assert!(initial.0.is_some() && initial.1.is_some());
- let outcome = apply_icloud_health_blocker_to_projection(
+ let outcome = apply_provider_blocker_to_projection(
&receipt,
&adr_dir,
&goal_dir,
@@ -3351,6 +3367,56 @@ mod tests {
.any(|value| value == "provider-state-blocked:icloud-native-status-pending-scan"));
}
+ #[cfg(not(coverage))]
+ #[test]
+ fn provider_global_sync_blocker_updates_google_drive_goal_and_adr_projections() {
+ let temporary = tempfile::tempdir().unwrap();
+ let receipt = cloud_transfer::CloudCopyReceipt {
+ version: cloud_transfer::RECEIPT_VERSION,
+ receipt_id: "e".repeat(64),
+ candidate_fingerprint: "f".repeat(64),
+ provider: cloud::CloudProvider::GoogleDrive,
+ source: "/source/file.zip".into(),
+ destination: "/google-drive/file.zip".into(),
+ bytes: 1,
+ blake3: "a".repeat(64),
+ sha256: "b".repeat(64),
+ quick_xor_base64: String::new(),
+ source_modified_ms: 1,
+ copied_at_ms: 2,
+ copy_verified: true,
+ provider_sync_confirmed: false,
+ lineage_fingerprint: None,
+ lineage: None,
+ };
+ let adr_dir = temporary.path().join("adr");
+ let goal_dir = temporary.path().join("goals");
+ let outcome = apply_provider_blocker_to_projection(
+ &receipt,
+ &adr_dir,
+ &goal_dir,
+ 3,
+ "provider-global-sync-temporarily-disconnected",
+ );
+ assert!(outcome.wrote);
+ assert!(outcome.warnings.is_empty());
+
+ let goal: cloud_adr::CloudOffloadGoalSnapshot = serde_json::from_slice(
+ &std::fs::read(goal_dir.join(format!("{}-latest.json", receipt.receipt_id))).unwrap(),
+ )
+ .unwrap();
+ assert_eq!(goal.status, "blocked");
+ assert!(!goal.completion_gates["provider-sync-state-complete"]);
+ assert!(!goal.completion_gates["explicit-eviction-permit"]);
+ let adr: cloud_adr::CloudOffloadAdrSnapshot = serde_json::from_slice(
+ &std::fs::read(adr_dir.join(format!("{}-latest.json", receipt.receipt_id))).unwrap(),
+ )
+ .unwrap();
+ assert!(adr.consequences.iter().any(|value| {
+ value == "provider-state-blocked:provider-global-sync-temporarily-disconnected"
+ }));
+ }
+
#[cfg(not(coverage))]
#[test]
fn reconciliation_without_receipts_is_read_only() {
From 7edae17fa5bfc026d2ec5099c9bbd1578ee8518e Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 09:39:39 +0900
Subject: [PATCH 67/86] docs(cloud): record provider blocker projection
---
.../adr/0001-cloud-offload-goal-state.md | 18 ++++++++++++++++++
docs/product-technical-gap-baseline.md | 17 ++++++++++++++++-
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 32db5e5e9..d10907117 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -887,3 +887,21 @@ eviction. No Finder cancellation, provider restart, cloud write, source mutation
performed. The observation was made against DiskSage PR #247 exact head
`9fdf2922da2939d96d3c2393539f2b2d42009929`; the PR remains draft, review-required, and blocked while
hosted checks are pending.
+
+## Amendment: project third-party provider blockers into runtime Goal/ADR (2026-08-25 09:30 +0900)
+
+The provider-global sync persistence path now reuses the monotonic projection helper for OneDrive
+and Google Drive as well as iCloud. A fresh `temporarily disconnected`, server-unreachable,
+transfer-active, or reconciliation-pending observation therefore writes the matching receipt-linked
+Goal as `blocked`, closes `provider-sync-state-complete` and `explicit-eviction-permit`, and records
+`provider-state-blocked:` in its paired ADR. A clear report never rewrites projections, and
+missing or malformed receipts remain an explicit bounded warning. This is local evidence/projection
+state only; no Finder cancellation, provider restart, cloud write, source mutation, attestation, or
+eviction was executed.
+
+After reclaiming only DiskSage's disposable Rust build artifacts, the root volume had about 3.5 GiB
+free, while the same Google Drive dump still reported `temporarily disconnected`, `-1004`, active
+transfer markers, and a 2,000-entry reconciliation section. The persisted diagnosis is therefore
+not reduced to local disk pressure. The implementation was verified at PR #247 exact head
+`87c9089bcd4af49f8f8751c54ebcc45b519d1f0c`; the draft PR remains review-required and blocked while
+hosted checks are pending.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 5b4403e5f..7dcee5d3a 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1446,4 +1446,19 @@ mutation is performed.
`9fdf2922da2939d96d3c2393539f2b2d42009929`; its hosted checks are still pending and the protected
PR remains draft/blocked/review-required. The host's `utun4` default route is recorded only as
context, not as a proven root cause. The filename dates `2026-04-28` and `251210` remain
- auxiliary production-time evidence; embedded metadata and context retain precedence.
+auxiliary production-time evidence; embedded metadata and context retain precedence.
+
+## 2026-08-25 09:30 +0900 third-party provider blocker projection
+
+- Provider-global sync persistence now applies the existing monotonic ADR/Goal projection contract
+ to OneDrive and Google Drive. A blocked provider observation sets the linked Goal to `blocked`,
+ revokes provider-sync and eviction gates, and records the stable blocker in the paired ADR;
+ clear observations do not rewrite state.
+- Reclaiming only disposable DiskSage Rust build artifacts increased root free space to about 3.5
+ GiB, but the same Google Drive dump still reported `temporarily disconnected`, File Provider
+ `-1004`, active transfer markers, and 2,000 reconciliation entries. This confirms the current
+ stall remains provider-global rather than proven local fullness. No Finder/provider/source/cloud
+ mutation was performed.
+- The exact DiskSage PR #247 head is `87c9089bcd4af49f8f8751c54ebcc45b519d1f0c`; hosted checks are
+ pending and the protected PR remains draft/blocked/review-required. Filename dates
+ `2026-04-28` and `251210` remain auxiliary production-time evidence only.
From 5c3b87359103b82df3efb4099668b1b17f532259 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 10:09:01 +0900
Subject: [PATCH 68/86] fix(cloud): keep unverified preview headroom diagnostic
---
src-tauri/src/cloud_plan_view.rs | 47 ++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/src-tauri/src/cloud_plan_view.rs b/src-tauri/src/cloud_plan_view.rs
index 261a07eae..ea5aca9a7 100644
--- a/src-tauri/src/cloud_plan_view.rs
+++ b/src-tauri/src/cloud_plan_view.rs
@@ -40,6 +40,19 @@ pub fn normalize_native_copy_headroom_notices(report: &mut CloudPlanReport) {
return;
}
+ // An unsafe destination ancestor is a preview diagnostic, not a copy denial. The mutation
+ // boundary re-probes the exact staging path immediately before staging; keep the candidate
+ // selectable so the UI can surface that authoritative check instead of hiding it here.
+ for candidate in &mut report.candidates {
+ if candidate
+ .blocked_reason
+ .as_deref()
+ .is_some_and(|reason| reason.starts_with("local-volume-headroom-destination-"))
+ {
+ candidate.blocked_reason = None;
+ }
+ }
+
let has_verified_candidate = report
.candidates
.iter()
@@ -250,6 +263,40 @@ mod tests {
assert!(serialized["exact_copy_approval_phrase"].is_null());
}
+ #[test]
+ fn normalization_releases_unverified_destination_headroom_preview_block() {
+ let mut report = CloudPlanReport {
+ cloud_root: CloudRoot {
+ id: "google-drive-personal".into(),
+ provider: CloudProvider::GoogleDrive,
+ account_scope: CloudAccountScope::Personal,
+ label: "Google Drive".into(),
+ path: "/cloud".into(),
+ readable: true,
+ access_issue: None,
+ },
+ generated_at_ms: 42,
+ source_selection_policy: Some(CloudPlanOptions::default()),
+ candidates: vec![candidate(Some(
+ "local-volume-headroom-destination-parent-unsafe",
+ ))],
+ candidate_bytes: 4096,
+ potentially_reclaimable_bytes: 0,
+ exact_duplicates: ExactDuplicateSummary::default(),
+ capacity: None,
+ local_volume: None,
+ pre_copy_evidence: None,
+ notices: vec!["local-volume-headroom-unverified".into()],
+ };
+
+ normalize_native_copy_headroom_notices(&mut report);
+
+ assert_eq!(report.candidates[0].blocked_reason, None);
+ assert!(report
+ .notices
+ .contains(&"local-volume-headroom-unverified".to_string()));
+ }
+
#[test]
fn report_conversion_preserves_plan_evidence_and_enriches_candidates() {
let report = CloudPlanReport {
From 2ec4b8ff42ea0ac5baf2741e714689c248fb58dc Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 10:16:17 +0900
Subject: [PATCH 69/86] docs(cloud): bind headroom to data volume
---
.../adr/0001-cloud-offload-goal-state.md | 18 ++++++++++++++++++
docs/product-technical-gap-baseline.md | 14 ++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index d10907117..7559ce762 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -905,3 +905,21 @@ transfer markers, and a 2,000-entry reconciliation section. The persisted diagno
not reduced to local disk pressure. The implementation was verified at PR #247 exact head
`87c9089bcd4af49f8f8751c54ebcc45b519d1f0c`; the draft PR remains review-required and blocked while
hosted checks are pending.
+
+## Amendment: bind headroom evidence to the actual data volume (2026-08-25 10:14 +0900)
+
+The live host recheck distinguished the system volume from the `/Users` data volume used by the
+source and File Provider staging. `/Users` had about 594 MiB available before disposable build
+artifacts were cleaned, while `real_datasets` was about 7.2 GiB; after cleanup the same data volume
+had about 2.7 GiB available. The iCloud dump simultaneously reported `pending-indexable-count:
+490195`, upload/download progress entries stuck at `0.0000`, and a 482,470-entry reconciliation
+section. DiskSage
+therefore treats destination-volume headroom and provider-global state as independent blockers:
+`local-volume-headroom-insufficient` remains candidate-specific, while provider indexing/transfer
+blockers remain global. A system-root `df` result cannot authorize a copy staged on `/Users`.
+
+The preview adapter releases only unverified destination-ancestor diagnostics so a later mutation
+probe remains authoritative; insufficient headroom stays blocked. This preserves the existing
+legacy aggregate fallback and gives the UI candidate-scoped evidence without granting cloud-write,
+attestation, or source-eviction authority. Observation is read-only; no Finder, provider, source,
+or cloud mutation was performed. Exact implementation head: `5c3b87359103b82df3efb4099668b1b17f532259`.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 7dcee5d3a..9d1f72d14 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1462,3 +1462,17 @@ auxiliary production-time evidence; embedded metadata and context retain precede
- The exact DiskSage PR #247 head is `87c9089bcd4af49f8f8751c54ebcc45b519d1f0c`; hosted checks are
pending and the protected PR remains draft/blocked/review-required. Filename dates
`2026-04-28` and `251210` remain auxiliary production-time evidence only.
+
+## 2026-08-25 10:14 +0900 data-volume headroom and iCloud backlog recheck
+
+- `/Users` is the source/File Provider staging volume; it had about 594 MiB available before
+ disposable build-artifact cleanup and about 2.7 GiB after cleanup, while `real_datasets` is about
+ 7.2 GiB. The system-root `df` value is not a valid staging-volume authority.
+- iCloud File Provider reported `pending-indexable-count: 490195`, upload/download progress entries
+ stuck at `0.0000`, and a 482,470-entry reconciliation section. The Finder preparation operation therefore remains
+ `provider-sync-incomplete`; it is not treated as a cloud receipt or completed upload.
+- The Rust preview adapter now keeps unverified destination-ancestor results as diagnostics while
+ retaining candidate-specific insufficient-headroom blockers. Mutation-time destination probing
+ remains authoritative. Exact PR #247 head: `5c3b87359103b82df3efb4099668b1b17f532259`; hosted
+ checks are queued and the protected PR remains draft/blocked/review-required. No Finder,
+ provider, source, cloud, or eviction mutation was performed.
From a9c868a6e9c8d68a9c6ea6de381e188740b8f5db Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 10:19:09 +0900
Subject: [PATCH 70/86] docs(cloud): record repeated zero-progress receipt
---
.../architecture/adr/0001-cloud-offload-goal-state.md | 10 ++++++++++
docs/product-technical-gap-baseline.md | 11 +++++++++++
2 files changed, 21 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 7559ce762..7342c100c 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -923,3 +923,13 @@ probe remains authoritative; insufficient headroom stays blocked. This preserves
legacy aggregate fallback and gives the UI candidate-scoped evidence without granting cloud-write,
attestation, or source-eviction authority. Observation is read-only; no Finder, provider, source,
or cloud mutation was performed. Exact implementation head: `5c3b87359103b82df3efb4099668b1b17f532259`.
+
+## Amendment: repeated zero-progress iCloud receipt (2026-08-25 10:18 +0900)
+
+Two read-only probes 19 seconds apart observed `pending-indexable-count` rising from `492224` to
+`492507` and reconciliation from `484500` to `484783`; both retained upload and download markers
+at `Fraction completed: 0.0000`. No standalone `cp`, `ditto`, or `rsync` process was present; the
+visible preparation window is therefore attributed to Finder/File Provider coordination, not a
+DiskSage copy worker. This is a repeated provider-stall receipt: Goal remains
+`provider-sync-incomplete`, and copy, attestation, cloud-write, and source-eviction gates remain
+closed. No cancellation or provider/source/cloud mutation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 9d1f72d14..3867c4541 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1476,3 +1476,14 @@ auxiliary production-time evidence; embedded metadata and context retain precede
remains authoritative. Exact PR #247 head: `5c3b87359103b82df3efb4099668b1b17f532259`; hosted
checks are queued and the protected PR remains draft/blocked/review-required. No Finder,
provider, source, cloud, or eviction mutation was performed.
+
+## 2026-08-25 10:18 +0900 repeated zero-progress iCloud receipt
+
+- Two read-only iCloud probes 19 seconds apart increased `pending-indexable-count` from `492224` to
+ `492507` and reconciliation from `484500` to `484783`, while upload/download markers remained at
+ `Fraction completed: 0.0000`.
+- No standalone `cp`, `ditto`, or `rsync` process was present. The visible Finder preparation
+ window is therefore provider coordination evidence, not proof of a DiskSage copy worker or a
+ completed cloud write. Goal remains `provider-sync-incomplete`; copy, attestation, cloud-write,
+ and source-eviction gates stay closed. No cancellation or provider/source/cloud mutation was
+ performed.
From dc57a1539b82514f4ceb17ec0fca42ed23ae7988 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 10:46:37 +0900
Subject: [PATCH 71/86] test(cloud): make headroom normalization deterministic
---
src-tauri/src/cloud_plan_view.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src-tauri/src/cloud_plan_view.rs b/src-tauri/src/cloud_plan_view.rs
index ea5aca9a7..8f636da88 100644
--- a/src-tauri/src/cloud_plan_view.rs
+++ b/src-tauri/src/cloud_plan_view.rs
@@ -288,6 +288,8 @@ mod tests {
pre_copy_evidence: None,
notices: vec!["local-volume-headroom-unverified".into()],
};
+ report.candidates[0].bytes = u64::MAX;
+ report.candidate_bytes = u64::MAX;
normalize_native_copy_headroom_notices(&mut report);
From 8ad12e1e5b57944960b5389e4d2067f3fcd0e924 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 10:57:05 +0900
Subject: [PATCH 72/86] docs(cloud): record deterministic headroom proof
---
docs/architecture/adr/0001-cloud-offload-goal-state.md | 9 +++++++++
docs/product-technical-gap-baseline.md | 10 ++++++++++
2 files changed, 19 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 7342c100c..9dc5dbffa 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -933,3 +933,12 @@ visible preparation window is therefore attributed to Finder/File Provider coord
DiskSage copy worker. This is a repeated provider-stall receipt: Goal remains
`provider-sync-incomplete`, and copy, attestation, cloud-write, and source-eviction gates remain
closed. No cancellation or provider/source/cloud mutation was performed.
+
+## Amendment: deterministic preview-headroom regression fixture (2026-08-25 11:00 +0900)
+
+The candidate-scoped preview normalization behavior is unchanged. Its regression fixture now uses
+an intentionally unfit candidate size so the test cannot inherit the host runner's root-volume
+capacity when the synthetic destination has no existing ancestor. Pinned Rust 1.97.1 verification
+passed all 745 library tests plus one ignored live-provider test at PR #247 exact head
+`dc57a1539b82514f4ceb17ec0fca42ed23ae7988`; no cloud, provider, Finder, source, or eviction
+mutation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 3867c4541..f51f16a95 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1487,3 +1487,13 @@ auxiliary production-time evidence; embedded metadata and context retain precede
completed cloud write. Goal remains `provider-sync-incomplete`; copy, attestation, cloud-write,
and source-eviction gates stay closed. No cancellation or provider/source/cloud mutation was
performed.
+
+## 2026-08-25 11:00 +0900 deterministic headroom regression proof
+
+- The preview adapter's candidate-scoped behavior is unchanged. Its regression fixture now uses an
+ intentionally unfit candidate size, preventing the test from accidentally treating the host
+ runner's root filesystem as verified capacity when the synthetic destination has no existing
+ ancestor.
+- Pinned Rust 1.97.1 ran 745 library tests with one live-provider test ignored; the exact head is
+ `dc57a1539b82514f4ceb17ec0fca42ed23ae7988`. This is test evidence only and grants no cloud-write,
+ attestation, source-eviction, Finder-cancel, or provider-restart authority.
From a6a8f45df854f0ce959dd0d634c43cf8371f4607 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:00:51 +0900
Subject: [PATCH 73/86] docs(queue): record current exact-head handoff
---
docs/product-technical-gap-baseline.md | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index f51f16a95..4e938a935 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1497,3 +1497,15 @@ auxiliary production-time evidence; embedded metadata and context retain precede
- Pinned Rust 1.97.1 ran 745 library tests with one live-provider test ignored; the exact head is
`dc57a1539b82514f4ceb17ec0fca42ed23ae7988`. This is test evidence only and grants no cloud-write,
attestation, source-eviction, Finder-cancel, or provider-restart authority.
+
+## 2026-08-25 11:20 +0900 exact-head queue handoff
+
+- DiskSage PR #247 is now `8ad12e1e5b57944960b5389e4d2067f3fcd0e924`; its new hosted test, Strix,
+ Noema, and queue checks are pending. It remains draft, blocked, and review-required; the local
+ Rust proof above is not a substitute for hosted exact-head evidence or protected approval.
+- DiskSage PR #249 remains at `2f1d585398b85f3f1adb3783520ad70e7b4a9c3f`; the stale Strix failure was
+ explicitly rerun against the same head after central `.github#1318` moved the smoke contract to
+ main. Other substantive checks are green, but the rerun and protected reviews are pending.
+- Central `.github#1318` merged as `8fd471a31399a914d9cb22a840f4a4c68e010ea6`; `.github#1316` is
+ based on that head at `e4f9865a1b06978324f006ee3861b84953877d8b` and carries the remaining direct
+ OpenCode model-pool alignment. No merge or approval is inferred from queued checks or bot reviews.
From 8e98b74e3791484c15da9b806692606ddcb3ff13 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:07:37 +0900
Subject: [PATCH 74/86] docs(cloud): record current finder preparation receipt
---
.../adr/0001-cloud-offload-goal-state.md | 17 +++++++++++++++++
docs/product-technical-gap-baseline.md | 15 +++++++++++++++
2 files changed, 32 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 9dc5dbffa..df5f3c8fe 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -942,3 +942,20 @@ capacity when the synthetic destination has no existing ancestor. Pinned Rust 1.
passed all 745 library tests plus one ignored live-provider test at PR #247 exact head
`dc57a1539b82514f4ceb17ec0fca42ed23ae7988`; no cloud, provider, Finder, source, or eviction
mutation was performed.
+
+## Amendment: current Finder copy-preparation provider receipt (2026-08-25 11:06 +0900)
+
+A fresh read-only File Provider dump identifies the visible Finder preparation as a provider
+coordination stall, not a DiskSage copy worker. The Google Drive domain is `temporarily
+disconnected`; its root metadata fetch reports File Provider `-1004` (server unreachable), the
+reconciliation queue is capped at 2,000 entries, and the latest user-initiated root retry is about
+57 minutes old. Upload/download progress markers exist without a completed item receipt. The
+iCloud domain independently reports `pending-indexable-count: 505103`, upload/download progress
+at `0.0000`, `disk import: yes`, and 497,379 reconciliation entries. The data volume currently has
+about 20 GiB free, so the observed Finder wait is not itself proof of local disk exhaustion.
+
+DiskSage therefore keeps the operation at `provider-sync-incomplete`: the Finder “복사 준비 중”
+window is not a cloud-write receipt, and copy, attestation, source eviction, provider restart, and
+Finder cancellation remain fail-closed. No Finder, provider, source, cloud, or eviction mutation
+was performed. The evidence is read-only and path-free; filename dates remain secondary to embedded
+metadata and context.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 4e938a935..151f11d0f 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1509,3 +1509,18 @@ auxiliary production-time evidence; embedded metadata and context retain precede
- Central `.github#1318` merged as `8fd471a31399a914d9cb22a840f4a4c68e010ea6`; `.github#1316` is
based on that head at `e4f9865a1b06978324f006ee3861b84953877d8b` and carries the remaining direct
OpenCode model-pool alignment. No merge or approval is inferred from queued checks or bot reviews.
+
+## 2026-08-25 11:06 +0900 current Finder “복사 준비 중” receipt
+
+- A fresh bounded read-only File Provider dump reports Google Drive as `temporarily disconnected`;
+ the root metadata request returns File Provider `-1004` (server unreachable), the reconciliation
+ queue is capped at 2,000 entries, and the latest user-initiated root retry is approximately
+ 57 minutes old. Upload/download markers are present, but there is no per-item destination receipt.
+- iCloud is also backlogged: `pending-indexable-count` is 505,103, upload/download progress is
+ `0.0000`, `disk import` is active, and reconciliation contains 497,379 entries. The data volume
+ has approximately 20 GiB free at this observation, so the Finder wait is provider coordination,
+ not proof that the local volume is full.
+- DiskSage must display this as `provider-sync-incomplete` and keep cloud write, attestation, source
+ eviction, provider restart, and Finder cancellation blocked. The screenshot is not a cloud receipt;
+ no Finder, provider, source, cloud, or eviction mutation was performed. Filename dates remain
+ auxiliary evidence only; embedded metadata and context retain precedence.
From 0d1b604cb2e9a0b7a32e5ac7344859dff53eef3b Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:11:58 +0900
Subject: [PATCH 75/86] docs(cloud): record persistent provider stall recheck
---
.../adr/0001-cloud-offload-goal-state.md | 11 +++++++++++
docs/product-technical-gap-baseline.md | 16 ++++++++++++++++
2 files changed, 27 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index df5f3c8fe..4e2aaa108 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -959,3 +959,14 @@ window is not a cloud-write receipt, and copy, attestation, source eviction, pro
Finder cancellation remain fail-closed. No Finder, provider, source, cloud, or eviction mutation
was performed. The evidence is read-only and path-free; filename dates remain secondary to embedded
metadata and context.
+
+## Amendment: persistent provider stall recheck (2026-08-25 11:09 +0900)
+
+The next bounded read-only recheck still reports the same two provider blockers. Google Drive is
+temporarily disconnected with File Provider `-1004`, a 2,000-entry reconciliation cap, and active
+upload/download markers. iCloud has grown to `pending-indexable-count: 506044` and 498,320
+reconciliation entries while upload/download remain at `0.0000`; disk import and stream reset are
+still active. The data volume remains at approximately 20 GiB free. This confirms persistence of
+the provider coordination stall rather than a transient Finder rendering issue. The runtime Goal
+remains `provider-sync-incomplete`; no copy, attestation, eviction, Finder cancellation, provider
+restart, or cloud/source mutation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 151f11d0f..5a0b7ceff 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1524,3 +1524,19 @@ auxiliary production-time evidence; embedded metadata and context retain precede
eviction, provider restart, and Finder cancellation blocked. The screenshot is not a cloud receipt;
no Finder, provider, source, cloud, or eviction mutation was performed. Filename dates remain
auxiliary evidence only; embedded metadata and context retain precedence.
+
+## 2026-08-25 11:09 +0900 persistent provider stall recheck
+
+- The next bounded read-only probe still finds Google Drive temporarily disconnected with File
+ Provider `-1004`, a 2,000-entry reconciliation cap, and active upload/download markers. iCloud
+ grew to `pending-indexable-count` 506,044 and 498,320 reconciliation entries while both transfer
+ fractions remain `0.0000`; disk import and stream reset remain active.
+- The data volume remains approximately 20 GiB free. This is persistent provider coordination,
+ not evidence that the Finder dialog completed or that the local volume is full. DiskSage keeps
+ `provider-sync-incomplete`, cloud write, attestation, source eviction, provider restart, and
+ Finder cancellation blocked.
+- Current exact-head queue evidence: PR #247 `8e98b74e` (draft/blocked/review-required; hosted
+ checks pending), PR #246 `1972614e` (draft/blocked/review-required; prior Strix HTTP 429/404
+ infrastructure failure rerun requested), PR #249 `2f1d585` (draft/blocked/review-required;
+ Strix rerun pending), and central `.github#1316` `e4f9865a` (blocked with no qualifying approval;
+ required checks pending). No merge is inferred.
From dda0f1d5e56cde1a1ed8f7dca9785b54fded6469 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:24:28 +0900
Subject: [PATCH 76/86] fix(cloud): allow canceling provider indexing stalls
---
src/lib/CloudArchive.svelte | 1 +
src/lib/cloudArchiveAdmissionContract.test.ts | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index 60cc450f4..29a33b3a2 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -36,6 +36,7 @@
]);
const PROVIDER_FINDER_COPY_BLOCKERS = new Set([
"provider-global-sync-transfer-active",
+ "provider-global-sync-indexing-pending",
"provider-global-sync-reconciliation-pending",
"provider-global-sync-temporarily-disconnected",
"provider-global-sync-server-unreachable",
diff --git a/src/lib/cloudArchiveAdmissionContract.test.ts b/src/lib/cloudArchiveAdmissionContract.test.ts
index 9dad8d7cc..fdabc2b82 100644
--- a/src/lib/cloudArchiveAdmissionContract.test.ts
+++ b/src/lib/cloudArchiveAdmissionContract.test.ts
@@ -73,6 +73,7 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("cancellingFinderCopy || checkingIcloudHealth");
expect(source).toContain("canCancelFinderCopyForProviderGlobalSync");
expect(source).toContain("provider-global-sync-reconciliation-pending");
+ expect(source).toContain("provider-global-sync-indexing-pending");
expect(source).toContain("provider-global-sync-local-disk-full");
expect(source).toContain("provider-global-sync-item-not-found");
expect(source).toContain("cancellingFinderCopy || checkingProviderGlobalSync");
From 445215d25672639280c5134f295650d26958805e Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:25:07 +0900
Subject: [PATCH 77/86] docs(cloud): record indexing stall cancellation gap
---
docs/architecture/adr/0001-cloud-offload-goal-state.md | 8 ++++++++
docs/product-technical-gap-baseline.md | 9 +++++++++
2 files changed, 17 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index 4e2aaa108..ab4ce3241 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -970,3 +970,11 @@ still active. The data volume remains at approximately 20 GiB free. This confirm
the provider coordination stall rather than a transient Finder rendering issue. The runtime Goal
remains `provider-sync-incomplete`; no copy, attestation, eviction, Finder cancellation, provider
restart, or cloud/source mutation was performed.
+
+## Amendment: third-party provider indexing can expose bounded Finder cancellation (2026-08-25 11:24 +0900)
+
+The provider-global UI now treats `provider-global-sync-indexing-pending` as a Finder-copy blocker,
+alongside active transfer and reconciliation blockers. This closes the case where OneDrive or Google
+Drive reports only an indexing backlog: the user can request the existing bounded Finder Escape
+action, while cloud/provider/source mutation remains unchanged. The contract test and Svelte type
+check pass at exact head `dda0f1d5`; no automatic cancellation was performed.
diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md
index 5a0b7ceff..07c26d02c 100644
--- a/docs/product-technical-gap-baseline.md
+++ b/docs/product-technical-gap-baseline.md
@@ -1540,3 +1540,12 @@ auxiliary production-time evidence; embedded metadata and context retain precede
infrastructure failure rerun requested), PR #249 `2f1d585` (draft/blocked/review-required;
Strix rerun pending), and central `.github#1316` `e4f9865a` (blocked with no qualifying approval;
required checks pending). No merge is inferred.
+
+## 2026-08-25 11:24 +0900 provider-indexing Finder action gap closed
+
+- Provider-global indexing-only stalls now expose the same bounded Finder-cancel action as transfer
+ and reconciliation stalls. This covers OneDrive/Google Drive reports of
+ `provider-global-sync-indexing-pending` without treating the provider dump as a copy receipt.
+- Svelte type-check and the focused CloudArchive admission/timing tests passed. The exact PR #247
+ head is `dda0f1d5`; its hosted checks restart on the documentation head. No automatic Finder
+ cancellation, provider restart, cloud write, source mutation, or eviction was performed.
From 58e1dc52a93cd959daaea549a7f362eddc1601f5 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 11:58:16 +0900
Subject: [PATCH 78/86] fix(cloud): preserve provider api admission invariants
---
src-tauri/src/cloud_transfer.rs | 33 +++++++++++
src-tauri/src/commands.rs | 8 +++
src-tauri/src/icloud_sync_health.rs | 55 +++++++++++++++++--
src/lib/CloudArchive.svelte | 4 +-
src/lib/cloudArchiveAdmissionContract.test.ts | 1 +
5 files changed, 94 insertions(+), 7 deletions(-)
diff --git a/src-tauri/src/cloud_transfer.rs b/src-tauri/src/cloud_transfer.rs
index cac63bef3..22dd1769d 100644
--- a/src-tauri/src/cloud_transfer.rs
+++ b/src-tauri/src/cloud_transfer.rs
@@ -648,6 +648,28 @@ pub fn candidate_blockers_with_review(
candidate_blockers_for_action(candidate, cloud_root, review_decision, false)
}
+/// Validate a provider-API copy while allowing only native staging headroom diagnostics.
+///
+/// Provider API uploads stream the source directly to the remote service and do not create the
+/// local File Provider staging file whose capacity probe produced `local-volume-headroom-*`.
+/// Every other planner blocker remains authoritative, including review, path, provider, and
+/// metadata gates.
+pub fn provider_api_candidate_blockers_with_review(
+ candidate: &CloudCandidate,
+ cloud_root: &CloudRoot,
+ review_decision: Option<&CloudReviewDecision>,
+) -> Vec {
+ let mut blockers = candidate_blockers_for_action(candidate, cloud_root, review_decision, false);
+ if candidate
+ .blocked_reason
+ .as_deref()
+ .is_some_and(|reason| reason.starts_with("local-volume-headroom-"))
+ {
+ blockers.retain(|blocker| blocker != "planner-blocked");
+ }
+ blockers
+}
+
/// Validate a fresh planner candidate for adopting a destination that already exists. This clears
/// only the exact `destination-exists` planner condition; every metadata, review, account-scope,
/// and path gate remains identical to a DiskSage-created copy.
@@ -2152,6 +2174,17 @@ mod tests {
.contains(&"source-already-in-cloud-root".to_string()));
}
+ #[test]
+ fn provider_api_copy_bypasses_only_native_staging_headroom() {
+ let mut candidate = candidate();
+ candidate.blocked_reason = Some("local-volume-headroom-insufficient".into());
+ assert!(provider_api_candidate_blockers_with_review(&candidate, &root(), None).is_empty());
+
+ candidate.blocked_reason = Some("destination-exists".into());
+ assert!(provider_api_candidate_blockers_with_review(&candidate, &root(), None)
+ .contains(&"planner-blocked".to_string()));
+ }
+
#[test]
#[cfg(not(coverage))]
fn production_copy_entrypoints_recheck_approval_age_against_live_time() {
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 7719d96d7..6b9bbdbc1 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -2063,6 +2063,14 @@ fn create_cloud_candidate_provider_api_receipt(
} else {
None
};
+ let blockers = cloud_transfer::provider_api_candidate_blockers_with_review(
+ candidate,
+ &selected,
+ review_decision.as_ref(),
+ );
+ if !blockers.is_empty() {
+ return Err(format!("provider-api-candidate-blocked:{}", blockers.join(",")));
+ }
let copy_approval = cloud_transfer::create_cloud_copy_approval(
candidate,
&selected,
diff --git a/src-tauri/src/icloud_sync_health.rs b/src-tauri/src/icloud_sync_health.rs
index dcd2c5fe5..dace12173 100644
--- a/src-tauri/src/icloud_sync_health.rs
+++ b/src-tauri/src/icloud_sync_health.rs
@@ -344,12 +344,12 @@ fn health_evidence_fingerprint(
Ok(digest.iter().map(|byte| format!("{byte:02x}")).collect())
}
-/// Recompute the pre-`pending_indexable_count` fingerprint without changing field order.
+/// Recompute the fingerprint used before the added aggregate provider counters existed.
///
/// Retained snapshots are immutable evidence. Accepting this one historical encoding keeps an
/// upgrade from silently shortening the durable stall clock while still requiring the exact old
/// digest; newly written snapshots continue to use `health_evidence_fingerprint`.
-fn health_evidence_fingerprint_without_pending_indexable(
+fn health_evidence_fingerprint_without_added_counters(
snapshot: &IcloudSyncHealthEvidenceSnapshot,
) -> Result {
let mut unsigned = snapshot.clone();
@@ -368,6 +368,18 @@ fn health_evidence_fingerprint_without_pending_indexable(
.ok_or_else(|| "icloud-sync-health-evidence-legacy-field-missing".to_string())?;
encoded.drain(index..index + field.len());
}
+ if unsigned
+ .native_status
+ .as_ref()
+ .is_some_and(|status| status.pending_scan_count == 0)
+ {
+ let field = b"\"pending_scan_count\":0,";
+ let index = encoded
+ .windows(field.len())
+ .position(|window| window == field)
+ .ok_or_else(|| "icloud-sync-health-evidence-legacy-field-missing".to_string())?;
+ encoded.drain(index..index + field.len());
+ }
let digest = Sha256::digest(encoded);
Ok(digest.iter().map(|byte| format!("{byte:02x}")).collect())
}
@@ -496,8 +508,12 @@ pub fn validate_icloud_sync_health_evidence_snapshot(
let legacy_expected = (snapshot
.file_provider_activity
.as_ref()
- .is_some_and(|activity| activity.pending_indexable_count.is_none()))
- .then(|| health_evidence_fingerprint_without_pending_indexable(snapshot));
+ .is_some_and(|activity| activity.pending_indexable_count.is_none())
+ || snapshot
+ .native_status
+ .as_ref()
+ .is_some_and(|status| status.pending_scan_count == 0))
+ .then(|| health_evidence_fingerprint_without_added_counters(snapshot));
let fingerprint_matches = snapshot.evidence_fingerprint_sha256 == expected
|| legacy_expected
.and_then(Result::ok)
@@ -2835,7 +2851,7 @@ mod tests {
}
#[test]
- fn health_evidence_accepts_pre_pending_indexable_fingerprint() {
+ fn health_evidence_accepts_pre_added_counter_fingerprint() {
let mut report = build_report(
1,
vec![],
@@ -2863,9 +2879,25 @@ mod tests {
active_download_progress_millionths: None,
notices: vec!["test-notice".into()],
});
+ report.native_status = Some(IcloudNativeStatusEvidence {
+ schema_version: ICLOUD_NATIVE_STATUS_SCHEMA_VERSION,
+ observed_at_ms: 1,
+ command_succeeded: true,
+ timed_out: false,
+ output_truncated: false,
+ status_observed: true,
+ evidence_complete: true,
+ container_count: Some(1),
+ client_state: Some("ready".into()),
+ server_state: Some("ready".into()),
+ sync_state: Some("ready".into()),
+ last_sync_present: false,
+ pending_scan_count: 0,
+ notices: vec!["test-notice".into()],
+ });
let mut snapshot = health_evidence_snapshot_from_report(&report).unwrap();
snapshot.evidence_fingerprint_sha256 =
- health_evidence_fingerprint_without_pending_indexable(&snapshot).unwrap();
+ health_evidence_fingerprint_without_added_counters(&snapshot).unwrap();
validate_icloud_sync_health_evidence_snapshot(&snapshot).unwrap();
snapshot
@@ -2877,6 +2909,17 @@ mod tests {
validate_icloud_sync_health_evidence_snapshot(&snapshot).unwrap_err(),
"icloud-sync-health-evidence-fingerprint-invalid"
);
+
+ snapshot
+ .file_provider_activity
+ .as_mut()
+ .unwrap()
+ .pending_indexable_count = None;
+ snapshot.native_status.as_mut().unwrap().pending_scan_count = 1;
+ assert_eq!(
+ validate_icloud_sync_health_evidence_snapshot(&snapshot).unwrap_err(),
+ "icloud-sync-health-evidence-fingerprint-invalid"
+ );
}
#[cfg(not(coverage))]
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index 29a33b3a2..c8e97e0d0 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -271,10 +271,12 @@
const embeddedHighConfidence = candidate.production_time_confidence === "high"
&& candidate.production_time_source.startsWith("embedded:");
const approvalPhrase = api.cloudCopyApprovalPhrase(candidate, "copy-only");
+ const onlyNativeStagingBlocker = candidate.blocked_reason === null
+ || candidate.blocked_reason.startsWith("local-volume-headroom-");
return selectedRootDetails()?.provider !== "icloud"
&& hasProviderAdmissionBlocker(report?.notices ?? [])
&& providerApiWriteConnected()
- && candidate.blocked_reason === null
+ && onlyNativeStagingBlocker
&& (!candidate.requires_review || exactApproval)
&& (embeddedHighConfidence || exactApproval)
&& api.cloudCapacityAllowsCopy(report?.capacity)
diff --git a/src/lib/cloudArchiveAdmissionContract.test.ts b/src/lib/cloudArchiveAdmissionContract.test.ts
index fdabc2b82..7954b8eb2 100644
--- a/src/lib/cloudArchiveAdmissionContract.test.ts
+++ b/src/lib/cloudArchiveAdmissionContract.test.ts
@@ -82,6 +82,7 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("권한이 없으면 요청만 실패하며 파일·클라우드 데이터는 변경되지 않습니다");
expect(source).toContain("local-volume-headroom-insufficient");
expect(source).toContain("local-volume-headroom-unverified");
+ expect(source).toContain("local-volume-headroom-");
expect(source).toContain("const candidateBlocked = candidate.blocked_reason");
expect(source).toContain("const hasPerCandidateEvidence = report?.candidates.some");
expect(source).toContain("!nativeCopyHeadroomBlocked(candidate)");
From b3e00c6a9bf13152562ccc50f2ed742b03f0bffa Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 16:04:36 +0900
Subject: [PATCH 79/86] fix(cloud): preserve unverified destination blockers
---
src-tauri/src/cloud_plan_view.rs | 35 +++++++++++--------
...cloud_plan_destination_headroom_runtime.rs | 5 ++-
2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/src-tauri/src/cloud_plan_view.rs b/src-tauri/src/cloud_plan_view.rs
index 8f636da88..bc15aa92a 100644
--- a/src-tauri/src/cloud_plan_view.rs
+++ b/src-tauri/src/cloud_plan_view.rs
@@ -40,19 +40,6 @@ pub fn normalize_native_copy_headroom_notices(report: &mut CloudPlanReport) {
return;
}
- // An unsafe destination ancestor is a preview diagnostic, not a copy denial. The mutation
- // boundary re-probes the exact staging path immediately before staging; keep the candidate
- // selectable so the UI can surface that authoritative check instead of hiding it here.
- for candidate in &mut report.candidates {
- if candidate
- .blocked_reason
- .as_deref()
- .is_some_and(|reason| reason.starts_with("local-volume-headroom-destination-"))
- {
- candidate.blocked_reason = None;
- }
- }
-
let has_verified_candidate = report
.candidates
.iter()
@@ -69,6 +56,21 @@ pub fn normalize_native_copy_headroom_notices(report: &mut CloudPlanReport) {
return;
}
+ // An unsafe destination ancestor is a preview diagnostic, not a copy denial, but only when
+ // another candidate has established destination headroom. Without that proof, retain the
+ // candidate blocker so the serialized action cannot advertise an approval phrase for an
+ // unverified staging path. The mutation boundary still re-probes the exact path immediately
+ // before staging.
+ for candidate in &mut report.candidates {
+ if candidate
+ .blocked_reason
+ .as_deref()
+ .is_some_and(|reason| reason.starts_with("local-volume-headroom-destination-"))
+ {
+ candidate.blocked_reason = None;
+ }
+ }
+
report
.notices
.retain(|notice| !PLAN_WIDE_HEADROOM_BLOCKERS.contains(¬ice.as_str()));
@@ -264,7 +266,7 @@ mod tests {
}
#[test]
- fn normalization_releases_unverified_destination_headroom_preview_block() {
+ fn normalization_keeps_unverified_destination_headroom_preview_block() {
let mut report = CloudPlanReport {
cloud_root: CloudRoot {
id: "google-drive-personal".into(),
@@ -293,7 +295,10 @@ mod tests {
normalize_native_copy_headroom_notices(&mut report);
- assert_eq!(report.candidates[0].blocked_reason, None);
+ assert_eq!(
+ report.candidates[0].blocked_reason.as_deref(),
+ Some("local-volume-headroom-destination-parent-unsafe")
+ );
assert!(report
.notices
.contains(&"local-volume-headroom-unverified".to_string()));
diff --git a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
index eb8bbe4fe..bf6dfafba 100644
--- a/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
+++ b/src-tauri/tests/cloud_plan_destination_headroom_runtime.rs
@@ -68,7 +68,10 @@ fn cloud_plan_preview_uses_destination_ancestor_authority_at_runtime() {
normalize_native_copy_headroom_notices(&mut report);
assert_eq!(report.candidates.len(), 1);
- assert_eq!(report.candidates[0].blocked_reason, None);
+ assert_eq!(
+ report.candidates[0].blocked_reason.as_deref(),
+ Some("local-volume-headroom-destination-parent-unsafe")
+ );
assert!(
report
.notices
From 56dcb1986e65deba735c8bdf5f517ad5374ac239 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 16:26:14 +0900
Subject: [PATCH 80/86] docs(adr): record destination blocker proof gate
---
docs/architecture/adr/0001-cloud-offload-goal-state.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/docs/architecture/adr/0001-cloud-offload-goal-state.md b/docs/architecture/adr/0001-cloud-offload-goal-state.md
index ab4ce3241..1a365c858 100644
--- a/docs/architecture/adr/0001-cloud-offload-goal-state.md
+++ b/docs/architecture/adr/0001-cloud-offload-goal-state.md
@@ -978,3 +978,12 @@ alongside active transfer and reconciliation blockers. This closes the case wher
Drive reports only an indexing backlog: the user can request the existing bounded Finder Escape
action, while cloud/provider/source mutation remains unchanged. The contract test and Svelte type
check pass at exact head `dda0f1d5`; no automatic cancellation was performed.
+
+## Amendment: retain unverified destination blockers until proof exists (2026-08-25)
+
+Cloud-plan presentation now checks for at least one previously unblocked candidate with verified
+destination/staging headroom before clearing any `local-volume-headroom-destination-*` diagnostic.
+When no candidate proves the staging filesystem, the candidate blocker and plan-wide fail-closed
+notice remain, so the serialized backend view cannot advertise a copy-only approval phrase for an
+unverified destination. The focused unit and runtime regressions pass at exact head
+`b3e00c6a9bf13152562ccc50f2ed742b03f0bffa`; mutation-time re-probing remains authoritative.
From 0de3f6a3672c6ff0f39726b7a4ddaeba757154b6 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Tue, 25 Aug 2026 17:00:59 +0900
Subject: [PATCH 81/86] fix(cloud): bind provider projections to receipt
contract
---
src-tauri/src/commands.rs | 62 ++++++++++++++++++++
src-tauri/src/icloud_sync_health.rs | 2 +-
src-tauri/src/naruon_cloud_copy_readiness.rs | 5 +-
src-tauri/src/provider_evidence.rs | 3 +-
4 files changed, 68 insertions(+), 4 deletions(-)
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 6b9bbdbc1..83040742e 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -3425,6 +3425,68 @@ mod tests {
}));
}
+ #[cfg(not(coverage))]
+ #[test]
+ fn provider_goal_projection_scans_the_runtime_receipt_directory() {
+ let temporary = tempfile::tempdir().unwrap();
+ let receipt_dir = temporary.path().join("cloud-receipts");
+ let adr_dir = temporary.path().join("cloud-adr");
+ let goal_dir = temporary.path().join("cloud-goals");
+ let mut receipt = cloud_transfer::CloudCopyReceipt {
+ version: cloud_transfer::LEGACY_RECEIPT_VERSION,
+ receipt_id: String::new(),
+ candidate_fingerprint: "h".repeat(64),
+ provider: cloud::CloudProvider::GoogleDrive,
+ source: "/source/file.zip".into(),
+ destination: "/google-drive/file.zip".into(),
+ bytes: 1,
+ blake3: "i".repeat(64),
+ sha256: "j".repeat(64),
+ quick_xor_base64: String::new(),
+ source_modified_ms: 1,
+ copied_at_ms: 2,
+ copy_verified: true,
+ provider_sync_confirmed: false,
+ lineage_fingerprint: None,
+ lineage: None,
+ };
+ let mut receipt_id = blake3::Hasher::new();
+ receipt_id.update(&receipt.version.to_le_bytes());
+ receipt_id.update(receipt.candidate_fingerprint.as_bytes());
+ receipt_id.update(&[0]);
+ receipt_id.update(receipt.provider.as_str().as_bytes());
+ receipt_id.update(&[0]);
+ receipt_id.update(receipt.source.as_bytes());
+ receipt_id.update(&[0]);
+ receipt_id.update(receipt.destination.as_bytes());
+ receipt_id.update(&[0]);
+ receipt_id.update(&receipt.bytes.to_le_bytes());
+ receipt_id.update(receipt.blake3.as_bytes());
+ receipt_id.update(receipt.sha256.as_bytes());
+ receipt_id.update(receipt.quick_xor_base64.as_bytes());
+ receipt_id.update(&receipt.source_modified_ms.to_le_bytes());
+ receipt_id.update(&receipt.copied_at_ms.to_le_bytes());
+ receipt_id.update(&[receipt.copy_verified as u8, receipt.provider_sync_confirmed as u8]);
+ receipt.receipt_id = receipt_id.finalize().to_hex().to_string();
+
+ cloud_transfer::write_provider_api_receipt(&receipt, &receipt_dir).unwrap();
+ let notices = update_provider_goal_projections(
+ &receipt_dir,
+ &adr_dir,
+ &goal_dir,
+ 3,
+ cloud::CloudProvider::GoogleDrive,
+ "provider-global-sync-temporarily-disconnected",
+ );
+ assert!(notices.iter().any(|notice| notice == "dynamic-goal-projection-updated"));
+
+ let goal: cloud_adr::CloudOffloadGoalSnapshot = serde_json::from_slice(
+ &std::fs::read(goal_dir.join(format!("{}-latest.json", receipt.receipt_id))).unwrap(),
+ )
+ .unwrap();
+ assert_eq!(goal.status, "blocked");
+ }
+
#[cfg(not(coverage))]
#[test]
fn reconciliation_without_receipts_is_read_only() {
diff --git a/src-tauri/src/icloud_sync_health.rs b/src-tauri/src/icloud_sync_health.rs
index dace12173..210228754 100644
--- a/src-tauri/src/icloud_sync_health.rs
+++ b/src-tauri/src/icloud_sync_health.rs
@@ -56,7 +56,7 @@ pub const ICLOUD_NATIVE_STATUS_SCHEMA_VERSION: u32 = 1;
pub const ICLOUD_FILE_PROVIDER_ACTIVITY_SCHEMA_VERSION: u32 = 3;
pub const ICLOUD_SYNC_HEALTH_EVIDENCE_SCHEMA_VERSION: u32 = 1;
pub const ICLOUD_SYNC_HEALTH_EVIDENCE_DIRECTORY: &str = "icloud-sync-health-evidence";
-const FILE_PROVIDER_DISK_IMPORT_NOTICE: &str = "icloud-file-provider-disk-import-active";
+pub(crate) const FILE_PROVIDER_DISK_IMPORT_NOTICE: &str = "icloud-file-provider-disk-import-active";
const MAX_PERSISTED_HEALTH_SNAPSHOTS: usize = 128;
const MAX_PERSISTED_HEALTH_SNAPSHOT_BYTES: usize = 64 * 1024;
diff --git a/src-tauri/src/naruon_cloud_copy_readiness.rs b/src-tauri/src/naruon_cloud_copy_readiness.rs
index fc2ab170b..462655c3e 100644
--- a/src-tauri/src/naruon_cloud_copy_readiness.rs
+++ b/src-tauri/src/naruon_cloud_copy_readiness.rs
@@ -18,6 +18,7 @@ use crate::icloud_sync_health::{
validate_native_status_evidence,
validate_file_provider_activity_evidence, IcloudFileProviderActivityEvidence,
IcloudNativeStatusEvidence, IcloudSyncHealthReport, ICLOUD_SYNC_HEALTH_SCHEMA_VERSION,
+ FILE_PROVIDER_DISK_IMPORT_NOTICE,
};
use crate::naruon_capacity;
use crate::provider_capacity::{self, CapacityEvidenceKind, CloudCapacityAssessment};
@@ -1217,9 +1218,9 @@ fn validate_icloud_admission_summary(
if activity
.notices
.iter()
- .any(|notice| notice == "icloud-file-provider-disk-import-active")
+ .any(|notice| notice == FILE_PROVIDER_DISK_IMPORT_NOTICE)
{
- expected.push("icloud-file-provider-disk-import-active".to_string());
+ expected.push(FILE_PROVIDER_DISK_IMPORT_NOTICE.to_string());
}
if !no_progress && !materialization_failed
&& (activity.active_upload_count > 0 || activity.active_download_count > 0)
diff --git a/src-tauri/src/provider_evidence.rs b/src-tauri/src/provider_evidence.rs
index f355365b7..e8ee10db2 100644
--- a/src-tauri/src/provider_evidence.rs
+++ b/src-tauri/src/provider_evidence.rs
@@ -234,7 +234,8 @@ fn prune_receipt_evidence_history(
///
/// The file is create-only, read-only, fsynced, and named by the receipt, observation time, and
/// integrity digest. Existing evidence is never overwritten. Repeated attestations retain the
-/// newest bounded per-receipt history so background reconciliation cannot grow storage forever.
+/// newest bounded per-receipt history so background reconciliation cannot grow storage forever;
+/// the just-written protected record is retained even if the system clock regresses.
#[cfg(not(coverage))]
pub fn write_immutable_sync_evidence(
directory: &Path,
From 67830c8742069d7eedace9348b482c3ff0ea6e19 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 26 Aug 2026 00:06:01 -0700
Subject: [PATCH 82/86] fix: verify Windows release artifact namespace
---
.github/scripts/verify-release-artifacts.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/scripts/verify-release-artifacts.sh b/.github/scripts/verify-release-artifacts.sh
index b35a74651..5d891302e 100644
--- a/.github/scripts/verify-release-artifacts.sh
+++ b/.github/scripts/verify-release-artifacts.sh
@@ -33,7 +33,7 @@ require_exactly_one_file() {
expected_dirs=(
"release-disksage-ubuntu-22.04-${run_attempt}"
- "release-disksage-windows-latest-${run_attempt}"
+ "release-disksage-windows-2022-${run_attempt}"
"release-disksage-macos-latest-${run_attempt}"
)
From aefa0868b6ed6bd982c01f4c1647cf91c8a689bb Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 26 Aug 2026 01:36:31 -0700
Subject: [PATCH 83/86] fix: bind release artifacts to platform directories
---
.github/scripts/verify-release-artifacts.sh | 38 +++++++++++----------
1 file changed, 20 insertions(+), 18 deletions(-)
diff --git a/.github/scripts/verify-release-artifacts.sh b/.github/scripts/verify-release-artifacts.sh
index 5d891302e..a6b344e16 100644
--- a/.github/scripts/verify-release-artifacts.sh
+++ b/.github/scripts/verify-release-artifacts.sh
@@ -23,10 +23,10 @@ require_exactly_one_path() {
}
require_exactly_one_file() {
- local file_name="$1" count=0 matched_path=""
- while IFS= read -r -d '' matched_path; do count=$((count + 1)); done < <(find "$artifact_root" -type f -name "$file_name" -print0)
+ local directory="$1" file_name="$2" count=0 matched_path=""
+ while IFS= read -r -d '' matched_path; do count=$((count + 1)); done < <(find "$artifact_root/$directory" -type f -name "$file_name" -print0)
if [[ $count -ne 1 ]]; then
- printf 'Expected exactly one release artifact named %s, found %s.\n' "$file_name" "$count" >&2
+ printf 'Expected exactly one release artifact named %s in %s, found %s.\n' "$file_name" "$directory" "$count" >&2
exit 1
fi
}
@@ -55,22 +55,24 @@ if [[ -n "$unexpected_entry" ]]; then
exit 1
fi
-require_exactly_one_path '*/bundle/deb/*.deb' 'Debian bundle'
-require_exactly_one_path '*/bundle/appimage/*.AppImage' 'AppImage bundle'
-require_exactly_one_path '*/bundle/msi/*.msi' 'Windows MSI bundle'
-require_exactly_one_path '*/bundle/nsis/*.exe' 'Windows NSIS bundle'
-require_exactly_one_path '*/bundle/dmg/*.dmg' 'macOS DMG bundle'
+require_exactly_one_path "$artifact_root/${expected_dirs[0]}/bundle/deb/*.deb" 'Debian bundle'
+require_exactly_one_path "$artifact_root/${expected_dirs[0]}/bundle/appimage/*.AppImage" 'AppImage bundle'
+require_exactly_one_path "$artifact_root/${expected_dirs[1]}/bundle/msi/*.msi" 'Windows MSI bundle'
+require_exactly_one_path "$artifact_root/${expected_dirs[1]}/bundle/nsis/*.exe" 'Windows NSIS bundle'
+require_exactly_one_path "$artifact_root/${expected_dirs[2]}/bundle/dmg/*.dmg" 'macOS DMG bundle'
-for required_name in \
- disksage-cloud-plan-linux-x86_64 \
- disksage-duplicate-audit-linux-x86_64 \
- disksage-cloud-plan-windows-x86_64.exe \
- disksage-duplicate-audit-windows-x86_64.exe \
- disksage-cloud-plan-macos-arm64 \
- disksage-duplicate-audit-macos-arm64; do
- require_exactly_one_file "$required_name"
- require_exactly_one_file "$required_name.sha256"
-done
+require_exactly_one_file "${expected_dirs[0]}" disksage-cloud-plan-linux-x86_64
+require_exactly_one_file "${expected_dirs[0]}" disksage-cloud-plan-linux-x86_64.sha256
+require_exactly_one_file "${expected_dirs[0]}" disksage-duplicate-audit-linux-x86_64
+require_exactly_one_file "${expected_dirs[0]}" disksage-duplicate-audit-linux-x86_64.sha256
+require_exactly_one_file "${expected_dirs[1]}" disksage-cloud-plan-windows-x86_64.exe
+require_exactly_one_file "${expected_dirs[1]}" disksage-cloud-plan-windows-x86_64.exe.sha256
+require_exactly_one_file "${expected_dirs[1]}" disksage-duplicate-audit-windows-x86_64.exe
+require_exactly_one_file "${expected_dirs[1]}" disksage-duplicate-audit-windows-x86_64.exe.sha256
+require_exactly_one_file "${expected_dirs[2]}" disksage-cloud-plan-macos-arm64
+require_exactly_one_file "${expected_dirs[2]}" disksage-cloud-plan-macos-arm64.sha256
+require_exactly_one_file "${expected_dirs[2]}" disksage-duplicate-audit-macos-arm64
+require_exactly_one_file "${expected_dirs[2]}" disksage-duplicate-audit-macos-arm64.sha256
checksum_files=()
checksum_file=""
From b9ad2bd846868dbf542e434c85dbd225255fa61b Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 26 Aug 2026 18:20:39 +0900
Subject: [PATCH 84/86] fix: verify tag artifacts before sbom
---
.github/workflows/release.yml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 980672cd7..30a69e363 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -259,6 +259,10 @@ jobs:
path: release-artifacts
merge-multiple: false
+ - name: Verify downloaded release artifact contract
+ shell: bash
+ run: bash .github/scripts/verify-release-artifacts.sh release-artifacts "${{ github.run_attempt }}"
+
- name: Generate and validate source-bound SBOM
shell: bash
run: |
From 629cd15cec2d9c7f154a658b64a86d95b84bdfd5 Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Wed, 26 Aug 2026 22:38:01 +0900
Subject: [PATCH 85/86] test: update iCloud readiness fixture fields
---
src-tauri/tests/naruon_locked_fileprovider_item.rs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src-tauri/tests/naruon_locked_fileprovider_item.rs b/src-tauri/tests/naruon_locked_fileprovider_item.rs
index ff3a0d4c3..9c649552c 100644
--- a/src-tauri/tests/naruon_locked_fileprovider_item.rs
+++ b/src-tauri/tests/naruon_locked_fileprovider_item.rs
@@ -57,6 +57,7 @@ fn locked_item_health() -> IcloudSyncHealthReport {
schema_version: ICLOUD_SYNC_HEALTH_SCHEMA_VERSION,
output_mode: "icloud-local-sync-health".into(),
observed_at_ms: 30,
+ admission_blocked_since_ms: None,
provider: "icloud".into(),
evidence_kind: "supplementary-local-cloud-docs-private-schema".into(),
evidence_complete: true,
@@ -84,6 +85,7 @@ fn locked_item_health() -> IcloudSyncHealthReport {
staged_item_missing_count: 0,
sync_excluded_filename_count: 0,
sync_excluded_root_count: 0,
+ pending_indexable_count: None,
active_upload_count: 0,
active_download_count: 0,
active_upload_progress_millionths: None,
From ff70f8159b82f05593c2ae7611ab3a5229ae886f Mon Sep 17 00:00:00 2001
From: Seongho Bae
Date: Sun, 30 Aug 2026 00:23:21 +0900
Subject: [PATCH 86/86] fix: make cloud sync guidance actionable
---
src/lib/CloudArchive.svelte | 134 +++++++++---------
src/lib/cloudArchiveAdmissionContract.test.ts | 45 ++++--
2 files changed, 100 insertions(+), 79 deletions(-)
diff --git a/src/lib/CloudArchive.svelte b/src/lib/CloudArchive.svelte
index ece4b5c86..c1bd7e507 100644
--- a/src/lib/CloudArchive.svelte
+++ b/src/lib/CloudArchive.svelte
@@ -868,20 +868,20 @@
"icloud-native-sync-up-pending": "macOS iCloud sync-up이 아직 끝나지 않음",
"icloud-native-sync-down-pending": "macOS iCloud sync-down이 아직 끝나지 않음",
"icloud-native-status-evidence-incomplete": "macOS iCloud 상태 증거가 불완전함",
- "icloud-native-status-command-timeout": "macOS iCloud 상태 확인이 시간 초과되어 복사를 보류함",
- "icloud-native-status-pending-scan": "macOS iCloud native scan 대기 항목이 있음",
- "icloud-file-provider-no-progress": "File Provider fetch/create 요청이 진행률 없이 정지함",
- "icloud-file-provider-materialization-failed": "File Provider 파일 materialization이 실패함(staged item 없음)",
- "icloud-file-provider-item-locked": "File Provider 항목이 전파 잠금 상태임",
- "icloud-file-provider-stalled": "File Provider 오래된 오류로 전송이 정지된 상태임",
+ "icloud-native-status-command-timeout": "iCloud 상태 확인이 늦어지고 있습니다. 잠시 후 다시 확인하세요.",
+ "icloud-native-status-pending-scan": "iCloud가 파일 목록을 준비 중입니다. 완료될 때까지 복사를 기다리세요.",
+ "icloud-file-provider-no-progress": "iCloud 요청이 진행되지 않습니다. Finder 복사를 취소한 뒤 다시 확인하세요.",
+ "icloud-file-provider-materialization-failed": "iCloud가 파일을 준비하지 못했습니다. Finder 복사를 취소한 뒤 다시 시도하세요.",
+ "icloud-file-provider-item-locked": "iCloud가 파일을 처리 중입니다. Finder 작업을 취소하고 잠시 후 다시 확인하세요.",
+ "icloud-file-provider-stalled": "iCloud 전송이 오래 멈춰 있습니다. Finder 복사를 취소한 뒤 다시 확인하세요.",
"icloud-file-provider-filename-excluded": "iCloud가 파일 이름 때문에 동기화에서 제외한 항목이 있음",
"icloud-file-provider-root-excluded": "iCloud가 동기화 루트에서 제외한 항목이 있음",
- "icloud-file-provider-indexing-pending": "iCloud File Provider 메타데이터 색인 대기 항목이 있음",
- "icloud-file-provider-disk-import-active": "macOS File Provider 디스크 가져오기가 진행 중임",
- "icloud-file-provider-transfer-active": "File Provider 기존 upload/download가 진행 중임",
- "icloud-file-provider-dump-timeout": "File Provider 상태 확인이 시간 초과됨",
- "icloud-file-provider-dump-output-truncated": "File Provider 상태 증거가 잘려 불완전함",
- "icloud-file-provider-evidence-unavailable": "File Provider 상태 증거를 확인할 수 없음",
+ "icloud-file-provider-indexing-pending": "iCloud가 파일 목록을 준비 중입니다. 기존 전송이 끝난 뒤 다시 확인하세요.",
+ "icloud-file-provider-disk-import-active": "iCloud가 로컬 파일을 정리 중입니다. 완료될 때까지 새 복사를 기다리세요.",
+ "icloud-file-provider-transfer-active": "iCloud의 기존 업로드 또는 다운로드가 끝난 뒤 다시 확인하세요.",
+ "icloud-file-provider-dump-timeout": "iCloud 상태 확인이 늦어지고 있습니다. 잠시 후 다시 확인하세요.",
+ "icloud-file-provider-dump-output-truncated": "iCloud 상태를 모두 확인하지 못했습니다. 잠시 후 다시 확인하세요.",
+ "icloud-file-provider-evidence-unavailable": "iCloud 상태를 확인할 수 없습니다. 연결과 여유 공간을 확인하세요.",
"icloud-item-error-octagon-not-signed-in": "iCloud 계정 인증이 필요함",
"icloud-item-error-older-than-24h": "iCloud 동기화 오류가 24시간 이상 지속됨",
};
@@ -891,8 +891,8 @@
function providerGlobalSyncBlockerLabel(blocker: string): string {
const labels: Record = {
"provider-global-sync-transfer-active": "전역 파일 전송이 진행 중임",
- "provider-global-sync-indexing-pending": "공급자 인덱싱이 끝나지 않음",
- "provider-global-sync-reconciliation-pending": "공급자 reconciliation 대기 항목이 있음",
+ "provider-global-sync-indexing-pending": "파일 목록 준비가 끝난 뒤 다시 확인하세요.",
+ "provider-global-sync-reconciliation-pending": "클라우드 확인 작업이 끝난 뒤 다시 확인하세요.",
"provider-global-sync-filename-too-long": "파일명 제한 오류가 있음",
"provider-global-sync-temporarily-disconnected": "공급자가 일시적으로 연결 해제됨",
"provider-global-sync-server-unreachable": "공급자 서버에 연결할 수 없음",
@@ -958,19 +958,19 @@
- 화면이 열려 있는 동안 클라우드 쓰기·원본 삭제 없이 provider 증거와 ADR/Goal을 갱신합니다. iCloud가 막히면 자동 확인은 최대 5분 간격으로 줄어듭니다.
+ 화면이 열려 있는 동안 파일을 변경하지 않고 동기화 상태를 갱신합니다. iCloud가 지연되면 5분 간격으로 다시 확인합니다.
{#if selectedRootDetails() && !selectedRootDetails()?.readable}
- 이 File Provider 루트는 현재 읽을 수 없습니다. 공급자 전역 상태 진단과 고정된 데스크톱 클라이언트 복구만 허용하며,
- 복사·attestation·원본 정리는 루트가 다시 읽힐 때까지 차단합니다.
+ 이 클라우드 폴더를 읽을 수 없습니다. 클라우드 앱을 다시 연 뒤 상태를 확인하세요.
+ 폴더를 다시 읽을 수 있을 때까지 복사와 원본 정리는 보류됩니다.
{/if}
{#if reconciliation}
재시작 후 영수증 재검증
- {reconciliation.receipts_seen}개 확인 · {reconciliation.attested_count}개 provider 증거 갱신 ·
+ {reconciliation.receipts_seen}개 확인 · {reconciliation.attested_count}개 업로드 상태 갱신 ·
{reconciliation.pending_count}개 업로드 대기 · {reconciliation.error_count}개 확인 실패
{#if reconciliation.incomplete_reconciliation} · {reconciliation.unprocessed_count}개 미처리{/if}
@@ -987,37 +987,37 @@
{/each}
{/if}
-
이 작업은 provider 증거와 동적 ADR/Goal만 갱신하며 클라우드 쓰기·원본 삭제는 수행하지 않습니다.
+
이 작업은 이전 복사 상태만 다시 확인하며 파일을 변경하거나 삭제하지 않습니다.
{/if}
{#if reconciliationError}
{reconciliationError}
{/if}
{#if icloudHealth}
- iCloud 새 복사 admission
+ iCloud 복사 준비 상태
- {icloudHealth.new_copy_admission_state === "clear" ? "새 복사 허용 가능" : "새 복사 차단"} ·
+ {icloudHealth.new_copy_admission_state === "clear" ? "지금 복사 가능" : "복사 보류"} ·
대기 {icloudHealth.upload_queue.scheduled_waiting_count}개 ·
진행 {icloudHealth.upload_queue.scheduled_active_count}개 ·
- sync-up 차단 {icloudHealth.upload_queue.blocked_on_sync_up_count}개 ·
+ 업로드 보류 {icloudHealth.upload_queue.blocked_on_sync_up_count}개 ·
오류 {icloudHealth.upload_queue.item_error_count}개
{#if icloudHealth.file_provider_activity}
- · File Provider 무진행 fetch {icloudHealth.file_provider_activity.no_progress_fetch_count}개 / create {icloudHealth.file_provider_activity.no_progress_create_count}개 ·
- materialization 실패 {icloudHealth.file_provider_activity.materialization_failure_count}개 / staged item 없음 {icloudHealth.file_provider_activity.staged_item_missing_count}개 ·
- 색인 대기 {icloudHealth.file_provider_activity.pending_indexable_count ?? 0}개 ·
- 디스크 import {icloudHealth.file_provider_activity.notices.includes("icloud-file-provider-disk-import-active") ? "진행 중" : "없음"} ·
- 활성 upload {icloudHealth.file_provider_activity.active_upload_count}개 / download {icloudHealth.file_provider_activity.active_download_count}개
+ · 응답 없는 요청 {icloudHealth.file_provider_activity.no_progress_fetch_count + icloudHealth.file_provider_activity.no_progress_create_count}개 ·
+ 파일 준비 실패 {icloudHealth.file_provider_activity.materialization_failure_count + icloudHealth.file_provider_activity.staged_item_missing_count}개 ·
+ 파일 목록 준비 {icloudHealth.file_provider_activity.pending_indexable_count ?? 0}개 ·
+ 로컬 파일 정리 {icloudHealth.file_provider_activity.notices.includes("icloud-file-provider-disk-import-active") ? "진행 중" : "없음"} ·
+ 기존 업로드 {icloudHealth.file_provider_activity.active_upload_count}개 / 다운로드 {icloudHealth.file_provider_activity.active_download_count}개
{#if providerProgressPercent(icloudHealth.file_provider_activity.active_upload_progress_millionths)}
- · upload 진행률 {providerProgressPercent(icloudHealth.file_provider_activity.active_upload_progress_millionths)}
+ · 업로드 진행률 {providerProgressPercent(icloudHealth.file_provider_activity.active_upload_progress_millionths)}
{/if}
{#if providerProgressPercent(icloudHealth.file_provider_activity.active_download_progress_millionths)}
- · download 진행률 {providerProgressPercent(icloudHealth.file_provider_activity.active_download_progress_millionths)}
+ · 다운로드 진행률 {providerProgressPercent(icloudHealth.file_provider_activity.active_download_progress_millionths)}
{/if}
{/if}
{#if icloudHealth.native_status}
- · native pending-scan 관찰 {icloudHealth.native_status.pending_scan_count ?? 0}개(경계 내)
+ · 추가 확인 대기 {icloudHealth.native_status.pending_scan_count ?? 0}개
{/if}
-
마지막 증거 확인: {evidenceObservedAt(icloudHealth.observed_at_ms)}
+
마지막 확인: {evidenceObservedAt(icloudHealth.observed_at_ms)}
- iCloud 동기화 요약 증거를 저장하지 못했습니다. 이번 관찰값은 표시하되 장기 비교에는 사용하지 않으며,
- 복사·원본 정리 판정은 현재 증거가 다시 저장될 때까지 보수적으로 유지합니다.
+ iCloud 상태 기록을 저장하지 못했습니다. 여유 공간을 확보한 뒤 “상태 다시 확인”을 누르세요.
+ 상태가 저장될 때까지 복사와 원본 정리는 보류됩니다.
- Finder가 “복사 준비 중”에서 멈춘 동안 File Provider의 no-progress 요청이 함께 관찰되었습니다. Finder에 남은 복사 대기는 취소하고,
- File Provider 상태가 정상으로 관찰된 뒤 DiskSage에서 새 계획을 다시 실행해야 합니다.
+ Finder가 “복사 준비 중”에서 멈춰 있습니다. Finder에 남은 복사 대기를 취소하고,
+ iCloud 전송이 정상화된 뒤 “상태 다시 확인”을 누르세요.
- macOS iCloud native 상태에서 pending-scan 항목이 관찰되었습니다(경계 내 {icloudHealth.native_status?.pending_scan_count}개).
- Finder의 “복사 준비 중”은 완료 영수증이 아니므로 scan 대기가 해소될 때까지 새 복사·attestation·원본 정리를 진행하지 않습니다.
+ iCloud가 아직 {icloudHealth.native_status?.pending_scan_count}개 항목을 확인 중입니다.
+ Finder 전송이 끝난 뒤 “상태 다시 확인”을 누르세요. 완료 전에는 새 복사와 원본 정리를 진행하지 않습니다.
- File Provider가 파일 materialization에 실패했거나 staged item을 잃었습니다. 현재 복사는 완료로 간주하지 않으며,
- 새 복사·attestation·원본 정리는 상태가 정상화될 때까지 차단합니다.
+ iCloud가 파일을 준비하지 못했습니다. Finder 복사를 취소하고 “상태 다시 확인”을 누르세요.
+ 상태가 정상화될 때까지 새 복사와 원본 정리는 보류됩니다.
- File Provider 항목의 전파 잠금 상태가 Finder 복사 준비 지연과 함께 관찰되었습니다. Finder의 대기 작업을 취소하고,
- 상태가 정상화된 뒤 DiskSage에서 새 복사를 다시 시작하십시오.
+ iCloud가 파일을 처리 중이라 Finder 복사가 기다리고 있습니다. Finder의 대기 작업을 취소하고,
+ 잠시 후 “상태 다시 확인”을 누르세요.
- File Provider 큐에서 15분 이상 묵은 fetch/create 오류가 관찰되었습니다. Finder의 “복사 준비 중” 작업을 취소하고,
- 상태가 정상화된 뒤 DiskSage에서 새 복사를 다시 시작하십시오.
+ iCloud 전송이 15분 이상 진행되지 않았습니다. Finder의 “복사 준비 중” 작업을 취소하고,
+ 잠시 후 “상태 다시 확인”을 누르세요.
- File Provider 상태 확인이 제한시간을 넘었습니다. Finder에 남은 복사 대기를 취소하고,
- DiskSage에서 상태를 다시 확인한 뒤 admission이 clear일 때만 새 복사를 시작하십시오.
+ iCloud 상태 확인이 제한시간을 넘었습니다. Finder에 남은 복사 대기를 취소하고,
+ “상태 다시 확인” 결과가 복사 가능일 때만 새 복사를 시작하세요.
- iCloud에 기존 전송이 진행 중입니다. 기존 upload/download가 끝나고 새 복사 admission이
- clear가 될 때까지 Finder 복사와 원본 정리를 진행하지 않습니다.
+ iCloud에 기존 전송이 진행 중입니다. 업로드와 다운로드가 끝난 뒤 “상태 다시 확인”을 누르세요.
+ 화면에 “지금 복사 가능”이 표시될 때까지 새 복사와 원본 정리는 보류됩니다.
- iCloud File Provider에 메타데이터 색인 대기 항목이 {icloudHealth.file_provider_activity?.pending_indexable_count}개 있습니다.
- Finder의 “복사 준비 중” 단계가 이 대기열을 기다릴 수 있으므로, 색인 대기와 기존 전송이 해소되기 전에는 복사를 완료로 간주하지 않습니다.
+ iCloud가 {icloudHealth.file_provider_activity?.pending_indexable_count}개 파일의 목록을 준비 중입니다.
+ 기존 전송이 끝난 뒤 “상태 다시 확인”을 누르세요. 그전에는 복사를 완료로 간주하지 않습니다.
- macOS File Provider가 디스크 가져오기 작업을 진행 중입니다. Finder의 “복사 준비 중”은 완료 영수증이 아니므로,
- 가져오기와 색인 대기가 해소될 때까지 새 복사·attestation·원본 정리를 시작하지 않습니다.
+ iCloud가 로컬 파일을 정리 중입니다. 작업이 끝난 뒤 “상태 다시 확인”을 누르세요.
+ 완료 전에는 새 복사와 원본 정리를 시작하지 않습니다.
동일한 iCloud 차단 상태가 15분 이상 지속되었습니다. Finder에 남은 복사 대기를 취소하고,
- iCloud 상태가 clear가 될 때까지 새 복사·attestation·원본 정리를 시작하지 마십시오.
+ “상태 다시 확인” 결과가 “지금 복사 가능”이 될 때까지 새 복사와 원본 정리를 시작하지 마세요.
{/if}
{:else}
-
iCloud 전역 업로드 대기열이 비어 있습니다. 개별 파일은 별도 provider 증거가 필요합니다.
+
iCloud 업로드 대기열이 비어 있습니다. 복사할 파일의 업로드 상태를 확인한 뒤 원본을 정리하세요.
읽기 전용 로컬 증거이며, 원격 용량·개별 파일 업로드 완료·원본 삭제 권한을 대신 증명하지 않습니다.
+
이 상태만으로 원격 여유 공간이나 개별 파일의 업로드 완료를 확인할 수 없습니다. 원본 정리 전에 파일별 상태를 확인하세요.
{/if}
{#if icloudHealthError}
iCloud 상태 확인: {icloudHealthError}
- iCloud File Provider 증거를 확인하지 못했습니다. Finder에 남은 복사 대기를 취소하고,
- 로컬 여유공간을 확보한 뒤 DiskSage에서 상태를 다시 확인하십시오.
+ iCloud 상태를 확인하지 못했습니다. Finder에 남은 복사 대기를 취소하고,
+ 로컬 여유 공간을 확보한 뒤 “상태 다시 확인”을 누르세요.
{/if}
{#if providerGlobalSync}
- {providerGlobalSync.provider} 전역 동기화 admission
+ {providerGlobalSync.provider} 복사 준비 상태
- {providerGlobalSync.state === "clear" && providerGlobalSync.blockers.length === 0 ? "새 복사 허용 가능" : "새 복사 차단"} ·
+ {providerGlobalSync.state === "clear" && providerGlobalSync.blockers.length === 0 ? "지금 복사 가능" : "복사 보류"} ·
업로드 전송 {providerGlobalSync.upload_progress_present ? "진행 중" : "없음"} ·
다운로드 전송 {providerGlobalSync.download_progress_present ? "진행 중" : "없음"}
{#if providerGlobalSync.pending_indexable_count !== null}
- · 인덱싱 대기 {providerGlobalSync.pending_indexable_count}개
+ · 파일 목록 준비 {providerGlobalSync.pending_indexable_count}개
{/if}
· 마지막 관찰 {evidenceObservedAt(providerGlobalSyncObservedAtMs)} ·
{providerGlobalSync.blockers.length === 0 ? "1분" : "5분"} 후 자동 재확인
@@ -1165,13 +1165,13 @@
{#if providerGlobalSyncBlockedSinceMs > 0 && providerGlobalSyncObservedAtMs - providerGlobalSyncBlockedSinceMs >= PROVIDER_STALL_WARNING_MS}
- 동일한 공급자 차단 상태가 15분 이상 지속되었습니다. Finder에 남은 복사 대기를 취소하고,
- 공급자 앱을 재기동한 뒤 상태가 clear가 될 때까지 새 복사·attestation·원본 정리를 시작하지 마십시오.
+ 동일한 클라우드 지연이 15분 이상 지속되었습니다. Finder에 남은 복사 대기를 취소하고,
+ 클라우드 앱을 다시 연 뒤 상태를 확인하세요. “지금 복사 가능”이 표시될 때까지 새 복사와 원본 정리는 보류됩니다.
읽기 전용 File Provider 집계 증거이며, 클라우드 쓰기·개별 파일 attestation·원본 삭제 권한을 대신 증명하지 않습니다.
+
이 상태 확인은 파일을 변경하지 않습니다. 원본 정리 전에 복사한 파일의 업로드 완료를 확인하세요.
{/if}
{#if providerGlobalSyncError}
-
공급자 전역 동기화 상태 확인: {providerGlobalSyncError}
+
클라우드 상태를 확인하지 못했습니다: {providerGlobalSyncError}
- 공급자 전역 증거를 확인하지 못했습니다. Finder에 남은 복사 대기를 취소하고,
- 공급자 앱이 정상으로 관찰될 때까지 새 복사·attestation·원본 정리를 시작하지 마십시오.
+ Finder에 남은 복사 대기를 취소하고 클라우드 앱을 다시 여세요.
+ “지금 복사 가능”이 표시될 때까지 새 복사와 원본 정리는 보류됩니다.
{/if}
{#if roots.some((root) => !root.readable)}
diff --git a/src/lib/cloudArchiveAdmissionContract.test.ts b/src/lib/cloudArchiveAdmissionContract.test.ts
index 2119fd100..7f1f04a5a 100644
--- a/src/lib/cloudArchiveAdmissionContract.test.ts
+++ b/src/lib/cloudArchiveAdmissionContract.test.ts
@@ -15,20 +15,20 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("시스템 관리 데이터를 삭제하지 않습니다");
expect(source).toContain("icloud-item-error-octagon-not-signed-in");
expect(source).toContain("동기화 진단:");
- expect(source).toContain("iCloud File Provider 증거를 확인하지 못했습니다.");
+ expect(source).toContain("iCloud 상태를 확인하지 못했습니다.");
expect(source).toContain("no_progress_create_count");
expect(source).toContain("pending_indexable_count");
expect(source).toContain("pending_scan_count");
expect(source).toContain("icloud-native-status-pending-scan");
expect(source).toContain("icloud-file-provider-indexing-pending");
expect(source).toContain("icloud-file-provider-disk-import-active");
- expect(source).toContain("디스크 import");
+ expect(source).toContain("로컬 파일 정리");
expect(source).toContain("providerProgressPercent");
expect(source).toContain("active_upload_progress_millionths");
- expect(source).toContain("Finder가 “복사 준비 중”에서 멈춘 동안 File Provider의 no-progress 요청이 함께 관찰되었습니다.");
+ expect(source).toContain("Finder가 “복사 준비 중”에서 멈춰 있습니다.");
expect(source).not.toContain("Finder가 “복사 준비 중”에서 멈춘 원인은");
- expect(source).toContain("Finder에 남은 복사 대기는 취소");
- expect(source).toContain("File Provider 상태 확인이 제한시간을 넘었습니다");
+ expect(source).toContain("Finder에 남은 복사 대기를 취소");
+ expect(source).toContain("iCloud 상태 확인이 늦어지고 있습니다. 잠시 후 다시 확인하세요.");
expect(source).toContain("Lineage 연결관계");
expect(source).toContain("검증 복사 영수증 → provider attestation → Goal/ADR");
expect(source).toContain('from "./cloudLineageExport"');
@@ -36,7 +36,7 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("path-free lineage JSON 내보내기");
expect(source).toContain("원본·목적지 경로 없이 stable content ID");
expect(source).toContain("candidate.metadata_fingerprint");
- expect(source).toContain("마지막 증거 확인:");
+ expect(source).toContain("마지막 확인:");
expect(source).toContain("evidenceObservedAt(icloudHealth.observed_at_ms)");
expect(source).toContain("ICLOUD_HEALTH_BLOCKED_RETRY_INTERVAL_MS");
expect(source).toContain("icloudHealthNextCheckAt");
@@ -64,19 +64,17 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).toContain("next.pending_indexable_count !== null && next.pending_indexable_count > 0");
expect(source).toContain("provider-global-sync-item-not-found");
expect(source).toContain("icloud-file-provider-item-locked");
- expect(source).toContain("File Provider 항목이 전파 잠금 상태임");
- expect(source).toContain("File Provider 항목의 전파 잠금 상태가 Finder 복사 준비 지연과 함께 관찰되었습니다.");
- expect(source).toContain("File Provider 큐에서 15분 이상 묵은 fetch/create 오류가 관찰되었습니다.");
+ expect(source).toContain("iCloud가 파일을 처리 중입니다. Finder 작업을 취소하고 잠시 후 다시 확인하세요.");
expect(source).toContain("icloud-file-provider-stalled");
expect(source).not.toContain("Finder의 복사 준비가 진행되지 않습니다.");
expect(source).toContain("동일 차단 지속");
- expect(source).toContain("동일한 공급자 차단 상태가 15분 이상 지속되었습니다.");
- expect(source).toContain("공급자 전역 증거를 확인하지 못했습니다.");
+ expect(source).toContain("동일한 클라우드 지연이 15분 이상 지속되었습니다.");
+ expect(source).toContain("클라우드 상태를 확인하지 못했습니다:");
expect(source).toContain("마지막 관찰 {evidenceObservedAt(providerGlobalSyncObservedAtMs)}");
expect(source).toContain('providerGlobalSync.blockers.length === 0 ? "1분" : "5분"');
expect(source).toContain("후 자동 재확인");
expect(source).toContain("접근 불가·진단만 가능");
- expect(source).toContain("공급자 전역 상태 진단과 고정된 데스크톱 클라이언트 복구만 허용");
+ expect(source).toContain("이 클라우드 폴더를 읽을 수 없습니다. 클라우드 앱을 다시 연 뒤 상태를 확인하세요.");
expect(source).toContain("!selectedRootDetails()?.readable");
expect(source).toContain("async function cancelFinderCopy()");
expect(source).toContain("await api.cancelFinderCopy();");
@@ -99,6 +97,29 @@ describe("CloudArchive iCloud admission contract", () => {
expect(source).not.toContain("api.localCopyHasHeadroom(report?.local_volume, candidate.bytes)");
});
+ it("keeps the iCloud status panel customer-facing and actionable", () => {
+ const source = readFileSync(resolve(repositoryRoot, "src/lib/CloudArchive.svelte"), "utf8");
+ const panelStart = source.indexOf("iCloud 복사 준비 상태");
+ const panelEnd = source.indexOf("{#if providerGlobalSync}", panelStart);
+
+ expect(panelStart).toBeGreaterThanOrEqual(0);
+ expect(panelEnd).toBeGreaterThan(panelStart);
+ const panel = source.slice(panelStart, panelEnd);
+ const customerCopy = panel.replaceAll(/\{[^}]*\}/gs, " ").replaceAll(/<[^>]*>/gs, " ");
+ expect(customerCopy).toContain("상태 다시 확인");
+ expect(customerCopy).toContain("Finder 복사");
+ for (const internalTerm of [
+ "File Provider",
+ "materialization",
+ "staged item",
+ "pending-scan",
+ "attestation",
+ " admission",
+ ]) {
+ expect(customerCopy).not.toContain(internalTerm);
+ }
+ });
+
it("exposes cancellation only for the cancellable native copy path", () => {
const source = readFileSync(resolve(repositoryRoot, "src/lib/CloudArchive.svelte"), "utf8");
const copyStart = source.indexOf("async function copyCandidate(candidate: api.CloudCandidate)");