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
5 changes: 3 additions & 2 deletions Engine/overlay/src/omarchy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,15 @@ def run_layout(
free_parts,
# Also keep tight macOS containers for exact shortfalls.
# Do not probe unrelated stubs or data-only containers that
# upstream did not consider resizable.
# upstream did not consider resizable. A stub carries the
# version of the macOS it was made from, so test stub too.
[
part for part in installer.parts
if part in resizable_parts or (
part.type == "Apple_APFS"
and part.container is not None
and part.os
and any(os.version for os in part.os)
and any(os.version and not os.stub for os in part.os)
)
] if is_gpt else [],
stub_size,
Expand Down
5 changes: 3 additions & 2 deletions Engine/overlay/tests/test_omarchy_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,12 @@ def test_inspect_keeps_tight_containers_filtered_out_by_upstream(self):
"OMARCHY_ENGINE_JOURNAL": str(self.journal_path),
})
runtime.journal.inspection("apple,j314s", "supported")
macos = [SimpleNamespace(version="15.7")]
macos = [SimpleNamespace(version="15.7", stub=False)]
tight = SimpleNamespace(type="Apple_APFS", container={"CapacityFree": 1}, os=macos)
roomy = SimpleNamespace(type="Apple_APFS", container={}, os=macos)
data = SimpleNamespace(name="data", type="Apple_APFS", container={}, os=[])
stub = SimpleNamespace(type="Apple_APFS", container={}, os=[SimpleNamespace(version=None)])
# Like upstream's OSInfo, a real stub reports its macOS version.
stub = SimpleNamespace(type="Apple_APFS", container={}, os=[SimpleNamespace(version="15.7", stub=True)])
upstream_data = SimpleNamespace(name="upstream-data", type="Apple_APFS", container={}, os=[])
recovery = SimpleNamespace(type="Apple_APFS_Recovery", container={}, os=macos)
unknown = SimpleNamespace(type="Apple_APFS", container=None, os=macos)
Expand Down
2 changes: 1 addition & 1 deletion Engine/rebuild-python-overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

BASE_SHA256 = '9e9277384b6c9e8b269cc79b1b24df7bfcdcbb898a596a677b74d1d18050aebe'
BASE_COMMIT = 'f0469cea0899f3efed8efead604174c7a53c4451'
VERSION = 'v0.9.2-omarchy.20'
VERSION = 'v0.9.2-omarchy.27'

_SPEC = importlib.util.spec_from_file_location(
'verify_source_lock', Path(__file__).resolve().parent / 'verify-source-lock.py')
Expand Down
14 changes: 7 additions & 7 deletions Engine/source-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
}
},
"downstream_overlay": {
"version": "v0.9.2-omarchy.20",
"version": "v0.9.2-omarchy.27",
"capability": "candidate_bound_full_os_stage_one_authenticated_recovery",
"engine_modes": [
"inspect",
Expand Down Expand Up @@ -196,7 +196,7 @@
{
"path": "overlay/src/omarchy_runtime.py",
"destination": "src/omarchy_runtime.py",
"sha256": "bda8b7baa37052770da32a23b058da143b8b646aed1701866b3ee0338ff6729e"
"sha256": "8d5296fe17fe1bac6340a8c2433d0968f9d7f32f262e8a927dd507ec1efb823d"
},
{
"path": "overlay/src/omarchy_stage1.py",
Expand Down Expand Up @@ -236,7 +236,7 @@
{
"path": "overlay/tests/test_omarchy_runtime.py",
"destination": "tests/test_omarchy_runtime.py",
"sha256": "d46c27fa44a74afcffdcbfa2ab64bc6ae470b8138760df181f28c013932d3cdc"
"sha256": "02f4a46d6159f7e189b6e2349dfe43184a6e99256a51a066dd95d02f0500ee57"
},
{
"path": "overlay/tests/test_omarchy_stage1.py",
Expand Down Expand Up @@ -270,13 +270,13 @@
},
{
"path": "rebuild-python-overlay.py",
"sha256": "f14f10690a2ae558ed8775e2c2ddb8e3ddc2f78f1d340f4899a18ecb47c3414a"
"sha256": "e4a6c4f9a3b5e9410d2511c16bf85899e9630713791acf8c59c669bce15e02d0"
}
],
"validation_artifact": {
"filename": "installer-v0.9.2-omarchy.20.tar.gz",
"size_bytes": 17839374,
"sha256": "7d7d87a934c128e501f8f6287d259195238ed1ae7953336b91738ff63514ea93",
"filename": "installer-v0.9.2-omarchy.27.tar.gz",
"size_bytes": 17839422,
"sha256": "4f9241b0139ba6ccdcfdb3484831002e07e36ba50274279c641ca2020593a15a",
"reproducibility_scope": "two-identical-python-overlay-repacks-authenticated-base",
"signature": "absent",
"metadata_sha256": "2e6181ce6b6e17c11039e04bfadade8d10ae0e11889885ad8c9960b68f179a5d"
Expand Down
4 changes: 2 additions & 2 deletions Packaging/build-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ helper_identifier="$INSTALLER_HELPER_IDENTIFIER"
app_name="$INSTALLER_APP_NAME.app"
app_executable_name="OmarchyAppleInstallerApp"
daemon_plist_name="$helper_identifier.plist"
engine_file_name="installer-v0.9.2-omarchy.20.tar.gz"
engine_digest="7d7d87a934c128e501f8f6287d259195238ed1ae7953336b91738ff63514ea93"
engine_file_name="installer-v0.9.2-omarchy.27.tar.gz"
engine_digest="4f9241b0139ba6ccdcfdb3484831002e07e36ba50274279c641ca2020593a15a"

if [[ $signing_identity == "-" ]]; then
client_requirement="identifier \"$app_identifier\""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,22 @@ public struct InstallerAllocationRecommendation:
// The engine's hard container floor already protects macOS. A second
// recommendation threshold can shrink the range as free space grows.
let usable = available - reservedBytes
let margin = min(
usable / Self.resizeDriftMarginDivisor,
Self.maximumResizeDriftMarginBytes
)
let marginCeiling = usable - margin
// The doubled size becomes the minimum only when it stays below the
// margin, so the divider keeps a range and the engine its drift room.
if let recommended = candidate.recommendedInstallBytes {
let alignedRecommended = Self.alignUp(recommended, unit: unit)
if alignedRecommended <= usable - (usable % unit) {
if alignedRecommended < marginCeiling - (marginCeiling % unit) {
minimum = alignedRecommended
}
}
let margin = min(
usable / Self.resizeDriftMarginDivisor,
Self.maximumResizeDriftMarginBytes
)
// The margin is best effort: on a tight disk keep what fits above the
// minimum rather than dropping a candidate the reserve still allows.
maximum = min(usable, max(usable - margin, minimum))
// partition floor rather than dropping a candidate the reserve allows.
maximum = min(usable, max(marginCeiling, minimum))
} else {
return nil
}
Expand Down
8 changes: 4 additions & 4 deletions Sources/OmarchyAppleInstaller/ValidationEngineArtifact.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
/// An installation engine fix ships in a catalog without rebuilding
/// and re-notarizing the app. Nothing may require them to be equal.
public struct ValidationEngineArtifactLocator: Sendable {
public static let version = "v0.9.2-omarchy.20"
public static let fileName = "installer-v0.9.2-omarchy.20.tar.gz"
public static let version = "v0.9.2-omarchy.27"
public static let fileName = "installer-v0.9.2-omarchy.27.tar.gz"
public static let expectedDigest =
"sha256:7d7d87a934c128e501f8f6287d259195238ed1ae7953336b91738ff63514ea93"
public static let expectedSizeBytes: UInt64 = 17_839_374
"sha256:4f9241b0139ba6ccdcfdb3484831002e07e36ba50274279c641ca2020593a15a"
public static let expectedSizeBytes: UInt64 = 17_839_422

public init() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ final class InstallerAllocationRecommendationTests: XCTestCase {
let recommendation = try InstallerAllocationRecommendation(
inventory: inventory, reservedBytes: 10 * gib
)
XCTAssertEqual(recommendation.minimumBytes, (available >= 72 ? 62 : 32) * gib)
// 62 GiB is adopted only once it fits under the 5% drift margin.
XCTAssertEqual(recommendation.minimumBytes, (available >= 76 ? 62 : 32) * gib)
// Near the floor the margin gives way so the install still fits; once
// the margin fits above the minimum, the divider can always move.
if (available - 10) * 19 > 32 * 20 {
XCTAssertGreaterThan(recommendation.maximumBytes, recommendation.minimumBytes)
}
XCTAssertGreaterThanOrEqual(recommendation.maximumBytes, previousMaximum)
XCTAssertLessThanOrEqual(recommendation.maximumBytes, (available - 10) * gib)
previousMaximum = recommendation.maximumBytes
Expand All @@ -41,6 +47,26 @@ final class InstallerAllocationRecommendationTests: XCTestCase {
}
}

func testRecommendedMinimumKeepsTheDriftMarginAndARange() throws {
// Each step of free space from just below to well above where the doubled
// size fits: the divider must move and the margin must survive whenever
// the doubled size is the minimum.
for quarters: UInt64 in 280...320 {
let available = quarters * gib / 4
let resize = candidate(
kind: "resize", source: "disk0s2", length: 200 * gib,
minimumInstall: 32 * gib, minimumContainer: 200 * gib - available,
recommendedInstall: 62 * gib)
let recommendation = try InstallerAllocationRecommendation(
inventory: inventory([resize]), reservedBytes: 10 * gib)
let usable = available - 10 * gib
XCTAssertGreaterThan(recommendation.maximumBytes, recommendation.minimumBytes)
if recommendation.minimumBytes == 62 * gib {
XCTAssertLessThanOrEqual(recommendation.maximumBytes, usable - usable / 20)
}
}
}

func testLegacyRecommendationDoesNotShrinkCeilingOrFreezeAtFloor() throws {
var previousMaximum: UInt64 = 0
for available: UInt64 in 31...100 {
Expand Down Expand Up @@ -681,7 +707,8 @@ final class InstallerAllocationRecommendationTests: XCTestCase {
length: UInt64,
minimumInstall: UInt64,
minimumContainer: UInt64 = 0,
identityDigest: String? = nil
identityDigest: String? = nil,
recommendedInstall: UInt64? = nil
) -> ValidatedEngineCandidate {
ValidatedEngineCandidate(
kind: kind,
Expand All @@ -690,7 +717,8 @@ final class InstallerAllocationRecommendationTests: XCTestCase {
lengthBytes: length,
minimumInstallBytes: minimumInstall,
minimumContainerBytes: minimumContainer,
identityDigest: identityDigest
identityDigest: identityDigest,
recommendedInstallBytes: recommendedInstall
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
func testM3CapableInspectionIdentityIsPinnedExactly() {
XCTAssertEqual(
ValidationEngineArtifactLocator.version,
"v0.9.2-omarchy.20"
"v0.9.2-omarchy.27"
)
XCTAssertEqual(
ValidationEngineArtifactLocator.fileName,
"installer-v0.9.2-omarchy.20.tar.gz"
"installer-v0.9.2-omarchy.27.tar.gz"
)
XCTAssertEqual(
ValidationEngineArtifactLocator.expectedDigest,
"sha256:7d7d87a934c128e501f8f6287d259195238ed1ae7953336b91738ff63514ea93"
"sha256:4f9241b0139ba6ccdcfdb3484831002e07e36ba50274279c641ca2020593a15a"
)
XCTAssertEqual(
ValidationEngineArtifactLocator.expectedSizeBytes,
17_839_374
17_839_422
)
}

Expand Down
8 changes: 7 additions & 1 deletion docs/extraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,10 @@ The pinned archive is `installer-v0.9.2-omarchy.20.tar.gz`, 17,839,374 bytes, SH

After merging current `main` on 2026-10-03, two repacks with `/usr/bin/python3` 3.9.6 again reproduced that exact pinned digest and size. A repack with Python 3.14.7 produced SHA-256 `e901371a4ba34d901a980b6f7586fc07d14234f9b50724ac4972e43ef35416af`, 17,839,417 bytes. All eight shipped downstream Python modules match the final repository sources byte for byte in both encodings, including the planner's disk-shortfall change. Reproduction must use the recorded interpreter before treating a different archive digest as stale source. The portable source-lock tests now check every downstream input and build recipe against its recorded digest, catching source changes without matching lock updates even when the upstream checkout is unavailable.

Ship installer **2.1.0 or later** together with the new engine and a signed catalog whose `installer.minimumVersion` is at least **2.1.0**. Both release-input templates carry this gate, and the catalog generator refuses a lower minimum for `.18` or newer engines in this lineage. Older installers then show the update-required message before decoding recommendation fields. The bundled inspection pins now select `.20`; signed production catalogs and frozen private-test catalogs are not changed by this source update. Artifact publication, catalog signing and physical installation qualification remain separate release steps.
## Stub probe and divider margin fixes

Engine `.27` stops probing stub containers. A stub carries the version of the macOS it was made from, so a versioned OS alone admitted it to the limits probes, and one failed probe of a stub on a Mac with an existing Asahi or Omarchy install failed the whole inventory. The filter now admits only versioned OSes that are not stubs. The pinned archive is `installer-v0.9.2-omarchy.27.tar.gz`, 17,839,422 bytes, SHA-256 `4f9241b0139ba6ccdcfdb3484831002e07e36ba50274279c641ca2020593a15a`. Two repacks with macOS `/usr/bin/python3` 3.9.6 produced identical bytes from the authenticated `.14` base and locked v0.9.2 checkout; compared with `.20`, only `omarchy_runtime.py` and `version.tag` changed. The bundled inspection pins and both release-input templates select `.27`.

Swift now withholds the resize drift margin before adopting the recommended (doubled) Omarchy size as the minimum. Adopting it first left a band of free-space states where the minimum equalled the maximum: the divider could not move and no margin remained for the engine's admission check.

Ship installer **2.1.0 or later** together with the new engine and a signed catalog whose `installer.minimumVersion` is at least **2.1.0**. Both release-input templates carry this gate, and the catalog generator refuses a lower minimum for `.18` or newer engines in this lineage. Older installers then show the update-required message before decoding recommendation fields. The bundled inspection pins now select `.27`; signed production catalogs and frozen private-test catalogs are not changed by this source update. Artifact publication, catalog signing and physical installation qualification remain separate release steps.
6 changes: 3 additions & 3 deletions scripts/release-inputs-aurora.template.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"payload_name": "omarchy-2026.09.13-aarch64-apple-silicon-aurora-os-package.zip",
"engine_name": "installer-v0.9.2-omarchy.20.tar.gz",
"engine_name": "installer-v0.9.2-omarchy.27.tar.gz",
"metadata_name": "installer_data.json",
"engine_version": "v0.9.2-omarchy.20",
"engine_version": "v0.9.2-omarchy.27",
"evidence_revision": "4.0.3-mac.2.20260913-aurora",
"asahi_installer_tag": "v0.9.2",
"asahi_installer_revision": "dffbb38ef0c00c0431c609ecd8a00f42deb5b24c",
Expand Down Expand Up @@ -47,6 +47,6 @@
"installer": {
"minimum_version": "2.1.0",
"latest_version": "2.1.0",
"download_url": "https://downloads.aicodelabs.com.au/omarchy-mac/installer/2.1.0/Omarchy-MX-Mac-Installer-2.1.0.pkg"
"download_url": "https://downloads.aicodelabs.com.au/omarchy-mac/installer/2.1.0/Omarchy-MX-Mac-Installer-2.1.0.zip"
}
}
6 changes: 3 additions & 3 deletions scripts/release-inputs.template.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"payload_name": "omarchy-2026.09.13-aarch64-apple-silicon-asahi-os-package.zip",
"engine_name": "installer-v0.9.2-omarchy.20.tar.gz",
"engine_name": "installer-v0.9.2-omarchy.27.tar.gz",
"metadata_name": "installer_data.json",
"engine_version": "v0.9.2-omarchy.20",
"engine_version": "v0.9.2-omarchy.27",
"evidence_revision": "4.0.3-mac.1.20260913",
"asahi_installer_tag": "v0.9.2",
"asahi_installer_revision": "dffbb38ef0c00c0431c609ecd8a00f42deb5b24c",
Expand Down Expand Up @@ -47,6 +47,6 @@
"installer": {
"minimum_version": "2.1.0",
"latest_version": "2.1.0",
"download_url": "https://downloads.aicodelabs.com.au/omarchy-mac/installer/2.1.0/Omarchy-MX-Mac-Installer-2.1.0.pkg"
"download_url": "https://downloads.aicodelabs.com.au/omarchy-mac/installer/2.1.0/Omarchy-MX-Mac-Installer-2.1.0.zip"
}
}
2 changes: 1 addition & 1 deletion test/shell.d/apple-installer-identity-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ import json, sys
template, base, stem = sys.argv[1:4]
installer = json.load(open(template))["installer"]
version = installer["latest_version"]
expected = f"{base}/installer/{version}/{stem}-{version}.pkg"
expected = f"{base}/installer/{version}/{stem}-{version}.zip"
if installer["download_url"] != expected:
sys.exit(f"{template}: download_url {installer['download_url']} is not {expected}")
PY
Expand Down
Loading