Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Delta.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Packaging/Delta.entitlements;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/Delta.app.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand All @@ -1010,7 +1010,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = Packaging/Delta.entitlements;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/Delta.app.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
Expand Down Expand Up @@ -1122,7 +1122,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = "$(DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS)";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS = Packaging/DeltaTimeMachineFS.entitlements;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Packaging/DeltaTimeMachineFS.Info.plist;
Expand Down Expand Up @@ -1238,7 +1238,7 @@
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/DeltaTimeMachineHelper.Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down Expand Up @@ -1274,7 +1274,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = "$(DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS)";
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
DELTA_FSKIT_CODE_SIGN_ENTITLEMENTS = Packaging/DeltaTimeMachineFS.entitlements;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Packaging/DeltaTimeMachineFS.Info.plist;
Expand Down Expand Up @@ -1309,7 +1309,7 @@
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
CURRENT_PROJECT_VERSION = 13;
CURRENT_PROJECT_VERSION = 14;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = Packaging/DeltaTimeMachineHelper.Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
1 change: 1 addition & 0 deletions Documentation/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Delta 0.4.0 adds native Time Machine-format destinations:
- Keeps a partial FSKit/APFS mount in an explicit cleanup-only state: storage telemetry can no longer erase a system-connection failure, and no UI or command surface offers Back Up Now until macOS returns the exact Time Machine destination identity.
- Places the privileged setup daemon in Apple's current `SMAppService` executable layout and rejects the obsolete `SMJobBless` helper location, preventing update-time launch constraints from being bound to a legacy bundle structure.
- Adds a fail-closed clean-install release gate for the exact notarized app: after native administrator approval, Delta must launch and authenticate the embedded privileged helper by Security code hash, and candidate-bound evidence must match the app, helper, source commit, immutable build, and canonical `/Applications/Delta.app` path before publishing.
- Shows one authoritative Review Login Items action in Permissions when Time Machine system support and scheduled backups need the same macOS recovery surface.

This release also restores live Activity output following:

Expand Down
9 changes: 5 additions & 4 deletions Documentation/VERIFICATION_REPORT.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Delta Verification Report

Date: 19 July 2026
Date: 20 July 2026

Host: macOS 26.5.2 (25F84)

Candidate: Delta 0.4.0 (13)
Candidate: Delta 0.4.0 (14)

Status: exact-candidate verification contract

## Outcome

Delta's source, deterministic acceptance harnesses, Developer ID build, stable installer, notarization flow, Sparkle packaging, privacy controls, and production-readiness checks are covered by the release contract below. The source identifies the next candidate as `0.4.0` (13).
Delta's source, deterministic acceptance harnesses, Developer ID build, stable installer, notarization flow, Sparkle packaging, privacy controls, and production-readiness checks are covered by the release contract below. The source identifies the next candidate as `0.4.0` (14).

A release must not be merged, tagged, or published until the complete exact-commit gate, separate app and DMG notarization, stapling, Gatekeeper checks, signed-update installation, manual acceptance matrix, and required genuine external-provider evidence have all passed for this version. Generated evidence under `dist/` is authoritative for the commit, installed path, CDHash, notarization IDs, artifact hashes, command results, and acceptance status; this tracked report deliberately does not duplicate volatile identity values or claim that an unverified source commit has passed.

Expand All @@ -35,6 +35,7 @@ A release must not be merged, tagged, or published until the complete exact-comm
- Removed Intel-sensitive `pipefail` handling from bundled-tool validation and made the crash-marker scan portable to clean macOS runners without ripgrep.
- Added Delta-managed Time Machine-format destinations backed by a provisioned FSKit extension, bounded local cache, authenticated remote generations, verified recovery-key retention, safe reconnect and removal, and the existing native Permissions guidance.
- Added a clean first-registration Time Machine system-support acceptance gate that uses the production Service Management and authenticated XPC path, binds evidence to the exact notarized installed candidate and helper code hashes, and prevents publishing from treating retained development registration state or a transient build location as release proof.
- Kept the Permissions surface to one authoritative Review Login Items action when Time Machine system support and scheduled backups both need attention, and bound the generated Xcode project and its source specification to the same immutable build identity.

## Automated verification contract

Expand Down Expand Up @@ -69,7 +70,7 @@ Finalization requires separate accepted Apple submissions for the app archive an

## Required release handoff

1. Build, install, and complete all exact-commit automated, signing, Apple notarization, stapling, Gatekeeper, and evidence checks for `0.4.0` (13).
1. Build, install, and complete all exact-commit automated, signing, Apple notarization, stapling, Gatekeeper, and evidence checks for `0.4.0` (14).
2. Complete every row of the exact-candidate manual acceptance matrix, including a real signed Sparkle upgrade and system-delivered notifications.
3. Provide and exercise genuine external mounted SMB or NFS, non-local SFTP, and non-local S3-compatible fixtures, plus any additional backend families intended for this release.
4. Obtain a fully passing production-readiness result before merging, tagging `v0.4.0`, or publishing.
11 changes: 10 additions & 1 deletion Scripts/lib/delta-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ delta_assert_clean_worktree() {

delta_assert_release_metadata() {
local root="$1"
local settings version build notes_heading tag
local settings version build project_versions project_builds notes_heading tag
settings="$(/usr/bin/xcodebuild \
-project "$root/Delta.xcodeproj" \
-scheme Delta \
Expand All @@ -307,6 +307,15 @@ delta_assert_release_metadata() {
[[ "$build" =~ ^[1-9][0-9]*$ ]] \
|| delta_fail "CURRENT_PROJECT_VERSION must be a positive integer: ${build:-missing}"

if [[ -f "$root/project.yml" ]]; then
project_versions="$(/usr/bin/awk '/MARKETING_VERSION:/ { print $2 }' "$root/project.yml" | /usr/bin/sort -u)"
project_builds="$(/usr/bin/awk '/CURRENT_PROJECT_VERSION:/ { print $2 }' "$root/project.yml" | /usr/bin/sort -u)"
[[ "$project_versions" == "$version" ]] \
|| delta_fail "project.yml MARKETING_VERSION values must all match $version"
[[ "$project_builds" == "$build" ]] \
|| delta_fail "project.yml CURRENT_PROJECT_VERSION values must all match $build"
fi

notes_heading="$(/usr/bin/sed -n '1p' "$root/Documentation/RELEASE_NOTES.md" 2>/dev/null || true)"
[[ "$notes_heading" == "# Delta $version" ]] \
|| delta_fail "release notes must start with '# Delta $version'"
Expand Down
2 changes: 1 addition & 1 deletion Scripts/manual-acceptance-items.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
manual_acceptance_items() {
cat <<'EOF'
install_identity Install identity and privacy stability Install /Applications/Delta.app, launch it, quit, relaunch, and confirm macOS privacy prompts remain stable across rebuilds signed by the same identity.
settings_surface Settings surface Confirm Settings shows plain-language Scheduled Backups status, not raw LaunchAgent or implementation status; the compact status summary matches Full Disk Access, schedules, Pause automatic runs, Password Access, updates, notifications, idle-sleep protection, and bundled backup-tool state; expand How Scheduled Backups Work and confirm it explains closed-window scheduling, macOS approval, user-level permissions, and policy checks without raw implementation status; Password Access exposes status, refresh, and Repair Password Access; Run Due Now uses the same rules as automatic scheduled runs when they are not paused; Open Activity jumps to live and saved job output for scheduled-run troubleshooting; Start at Login uses macOS Login Items separately from scheduled backups; Sparkle automatic checks and background downloads are configurable; reset buttons restore recommended backup and restore defaults; new-profile schedule defaults are configurable and seed new profiles without rewriting existing ones; backup freshness warnings, source-access warnings, destination-check warnings, local/mounted destination free-space warnings, and activity history retention are configurable or visible where appropriate.
settings_surface Settings surface Confirm Settings shows plain-language Scheduled Backups status, not raw LaunchAgent or implementation status; the compact status summary matches Full Disk Access, schedules, Pause automatic runs, Password Access, updates, notifications, idle-sleep protection, and bundled backup-tool state; expand How Scheduled Backups Work and confirm it explains closed-window scheduling, macOS approval, user-level permissions, and policy checks without raw implementation status; Password Access exposes status, refresh, and Repair Password Access; Run Due Now uses the same rules as automatic scheduled runs when they are not paused; Open Activity jumps to live and saved job output for scheduled-run troubleshooting; Start at Login uses macOS Login Items separately from scheduled backups; when Time Machine system support and Scheduled Backups both need Login Items attention, confirm the System Access card shows exactly one Review Login Items action; Sparkle automatic checks and background downloads are configurable; reset buttons restore recommended backup and restore defaults; new-profile schedule defaults are configurable and seed new profiles without rewriting existing ones; backup freshness warnings, source-access warnings, destination-check warnings, local/mounted destination free-space warnings, and activity history retention are configurable or visible where appropriate.
full_disk_access Full Disk Access From Settings, open Privacy & Security, add Delta manually when required, recheck access, and confirm the dashboard only shows Readiness when action is needed. With access off, confirm a Time Machine Connect action routes to Permissions before FSKit, DiskImages, or remote writes begin; with access on, confirm the exact installed app/helper arrangement can add and remove the verified destination through public `tmutil`.
scheduled_backups Scheduled Backups Confirm the automated scheduler acceptance report passed, then create an enabled scheduled profile in the UI, approve Delta in Login Items if macOS asks, turn on Pause automatic runs and confirm due runs do not start, resume automatic runs, quit the main window, wait for the scheduler interval, and confirm the scheduled run appears in Dashboard, Activity, and menu bar state after relaunch.
password_access Password access Use an app-managed destination and a destination with backend credentials. Confirm Settings and diagnostics show Password Access as Ready, then confirm a scheduled backup does not show interactive Keychain prompts after Repair Password Access has been run when needed.
Expand Down
25 changes: 19 additions & 6 deletions Sources/Delta/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3725,6 +3725,15 @@ struct SettingsView: View {
}
}

private var loginItemsActionPlacement: SettingsLoginItemsActionPlacement {
.resolve(
timeMachineSystemSupportNeedsAttention:
hasTimeMachineDestinations && !model.timeMachineSystemSupportIsCurrent,
scheduledBackupsNeedAttention:
scheduledBackupsPermissionPresentation == .needsAttention
)
}

private var passwordAccessPermissionPresentation: SettingsPermissionPresentation {
switch backgroundSecretAccessSummary.state {
case .ready:
Expand Down Expand Up @@ -3768,8 +3777,8 @@ struct SettingsView: View {
model.requestTimeMachineSystemAccess()
}
.buttonStyle(.borderedProminent)
Button("Review Login Items") {
model.openLoginItemsSettings()
if loginItemsActionPlacement == .timeMachineSystemSupport {
reviewLoginItemsPermissionAction
}
}
}
Expand Down Expand Up @@ -3797,10 +3806,14 @@ struct SettingsView: View {

@ViewBuilder
private var scheduledBackupsPermissionActions: some View {
if scheduledBackupsPermissionPresentation == .needsAttention {
Button("Review Login Items") {
model.openLoginItemsSettings()
}
if loginItemsActionPlacement == .scheduledBackups {
reviewLoginItemsPermissionAction
}
}

private var reviewLoginItemsPermissionAction: some View {
Button("Review Login Items") {
model.openLoginItemsSettings()
}
}

Expand Down
21 changes: 21 additions & 0 deletions Sources/DeltaCore/SettingsSurfaceContract.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import Foundation

public enum SettingsLoginItemsActionPlacement: Equatable, Sendable {
case timeMachineSystemSupport
case scheduledBackups
case hidden

public static func resolve(
timeMachineSystemSupportNeedsAttention: Bool,
scheduledBackupsNeedAttention: Bool
) -> Self {
if timeMachineSystemSupportNeedsAttention {
return .timeMachineSystemSupport
}
if scheduledBackupsNeedAttention {
return .scheduledBackups
}
return .hidden
}
}

public enum SettingsSurfaceContract {
public static let categoryGeneral = "General"
public static let categoryPermissions = "Permissions"
Expand Down Expand Up @@ -119,6 +138,7 @@ public enum SettingsSurfaceContract {
"Compact status summary",
"Run Due Now scheduled-backup action",
"Start at Login separate from Scheduled Backups",
"One Login Items recovery action per settings context",
"Sparkle automatic check and download controls",
"Idle-sleep protection",
"Expandable Scheduled Backups explanation",
Expand Down Expand Up @@ -183,6 +203,7 @@ public enum SettingsSurfaceContract {
require(requiredManualAcceptanceCoverage, contains: "No raw system service terminology", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Scheduled Backups activity shortcut", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Password access repair", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "One Login Items recovery action per settings context", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Destination free-space warning control", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Configurable new-profile schedule defaults", in: "manual coverage", failures: &failures)
require(requiredManualAcceptanceCoverage, contains: "Expandable Scheduled Backups explanation", in: "manual coverage", failures: &failures)
Expand Down
32 changes: 32 additions & 0 deletions Tests/DeltaCoreTests/SettingsSurfaceContractTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@ import XCTest
@testable import DeltaCore

final class SettingsSurfaceContractTests: XCTestCase {
func testLoginItemsActionHasOneAuthoritativePermissionRow() {
XCTAssertEqual(
SettingsLoginItemsActionPlacement.resolve(
timeMachineSystemSupportNeedsAttention: true,
scheduledBackupsNeedAttention: true
),
.timeMachineSystemSupport
)
XCTAssertEqual(
SettingsLoginItemsActionPlacement.resolve(
timeMachineSystemSupportNeedsAttention: true,
scheduledBackupsNeedAttention: false
),
.timeMachineSystemSupport
)
XCTAssertEqual(
SettingsLoginItemsActionPlacement.resolve(
timeMachineSystemSupportNeedsAttention: false,
scheduledBackupsNeedAttention: true
),
.scheduledBackups
)
XCTAssertEqual(
SettingsLoginItemsActionPlacement.resolve(
timeMachineSystemSupportNeedsAttention: false,
scheduledBackupsNeedAttention: false
),
.hidden
)
}

func testSettingsSurfaceContractCoversRequiredCategoriesAndSummary() {
XCTAssertEqual(
SettingsSurfaceContract.categoryTitles,
Expand Down Expand Up @@ -58,6 +89,7 @@ final class SettingsSurfaceContractTests: XCTestCase {
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Password access repair"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Compact status summary"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Run Due Now scheduled-backup action"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("One Login Items recovery action per settings context"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Expandable Scheduled Backups explanation"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Sparkle automatic check and download controls"))
XCTAssertTrue(SettingsSurfaceContract.requiredManualAcceptanceCoverage.contains("Idle-sleep protection"))
Expand Down
Loading