Castle Wall macOS drill, combined #596 + W7-1, 2026-06-18

SUPERSEDING-EVIDENCE NOTE (merged 2026-06-27 as a corrected historical record): the Test A “#596 FAIL” recorded below is a true account of the 2026-06-18 drill leg, and its central finding held up. The branch fix under test (PR #621) did NOT fix the crash because, as this doc correctly diagnosed, the crash lived at the SwiftUI App-lifecycle layer, not in ContentView. PR #621 was therefore closed unmerged. The #596 boot crash was later closed by a DIFFERENT real fix (the SwiftUI-lifecycle async-init fix), verified on Mini1 in the 2026-06-20 full-scope re-drill and confirmed by the 2026-06-22 boot-survival re-drill (F1 closed, 5/5 consecutive real reboots on the fixed build). W7-1 was also subsequently proven (signed-drill session 2026-06-25, v983). Read this doc as the historical record of why the #621 path was rejected, NOT as the current status of #596 (closed) or W7-1 (proven).

Operator: drill thread on the MBA signing host (Erik present for the boot legs, then away). This doc records hardware results for coordinator verification. Until the coordinator independently verifies, every verdict here is provisional and nothing is merged.

What was tested

One binary built from drill/596-w7-1-combined carrying both fixes:

Both fixes compile into the host-app binary (CastleWallHostApp / CastleWallMain), NOT the CastleWallExtension sysext. The sysext binary is byte-unaffected by either fix.

Drilled artifact (proof of replacement)

Item Value
Branch / source SHA drill/596-w7-1-combined at a71cd190571f (off session-start main 3df595ed; both fix commits present)
CFBundleVersion 856 (git rev-list --count HEAD); was 843 on Mini1
Notarization Accepted, submission f828b643-27c5-4a46-b57e-99d1d29cd3c5; stapled + validated; spctl accepted, source Notarized Developer ID
Host-app binary SHA-256 (built) 22b99822f87af86fb260b2503428a55c7e699d0a1e8989f7e1311f7fef09bcde
Host-app binary SHA-256 (Mini1 installed + running) 22b99822f87af86fb260b2503428a55c7e699d0a1e8989f7e1311f7fef09bcde (MATCH; pid confirmed running v856)
Sysext binary SHA-256 (built, embedded in host app) 5606f925a9551ec30f772cbf305903ed81f2f0316dfcc456258c42c644a25ec7
Sysext binary SHA-256 (Mini1 activated after deploy) 5606f925a9551ec30f772cbf305903ed81f2f0316dfcc456258c42c644a25ec7 (MATCH; v843 e01b3f04... now terminated, uninstalls on reboot)
Drill target host Mini1 (Agents-Mac-mini), macOS 26.5.1 (25F80), FileVault ON, operator uid 501

Proof of replacement PASS for both the host app and the sysext: the running binaries on Mini1 are byte-identical to the freshly built, notarized v856.

Test A, #596 host-app boot crash, N>=3: FAIL

The fixed v856 host app STILL crashes at boot with the identical pre-#596 signature.

Evidence: two crash reports on Mini1, both build 856, both at ~38 to 39 seconds uptime:

Both crash with EXC_CRASH / SIGABRT, abort() called, identical crashing stack:

abort()
AttributeGraph  AG::precondition_failure(...)
AttributeGraph  AG::Graph::value_set(...)
SwiftUI  closure #1 in AppDelegate.applicationDidChangeScreenParameters(_:)
SwiftUI  AppDelegate.applicationDidChangeScreenParameters(_:)
SwiftUI  @objc AppDelegate.applicationWillFinishLaunching(_:)
CoreFoundation  __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__

Boot 1 (Apple-menu restart, app reopened at login) was clean, but boots 2 reproduced the crash, so the crash is not eliminated. Test A acceptance is N>=3 consecutive clean boots; this is a FAIL at N=1.

Root cause: the fix is at the wrong layer

The abort has ZERO app-code frames. It aborts entirely inside SwiftUI’s own framework AppDelegate.applicationDidChangeScreenParameters, fired during applicationWillFinishLaunching (very early launch, before the scene graph is ready), where SwiftUI sets an AttributeGraph value mid-update and trips the precondition. The app uses the pure SwiftUI App lifecycle (CastleWallHostApp: App with WindowGroup, no NSApplicationDelegateAdaptor), so SwiftUI installs this internal AppDelegate. The branch fix only deferred ContentView’s .onChange handlers, which are not even instantiated this early and never appear in the stack. ContentView.swift changes cannot fix a crash that lives in SwiftUI’s launch-time screen-parameters handling.

Recommendation: do NOT merge PR #621 as a #596 fix. The real fix is at the SwiftUI App-lifecycle level. Design candidates are in Review/Sanctuary/596_SwiftUI_AppLifecycle_Crash_RealFix_Design_2026-06-18.md. Re-drill required after the real fix.

Test B, W7-1 initial arm: PARTIAL POSITIVE SIGNAL, not the acceptance

W7-1 acceptance is: a FRESH first arm against a just-activated provider arms within seconds (no loadFromPreferences timed out). That full acceptance was NOT run, because a full arm needs the ~/.sanctuary-drill-0611b fortress (absent, torn to baseline) plus its passphrase, plus Erik present for boot-cut recovery, and attempting it unattended risked stranding Mini1.

Positive signal captured: with v856 (RunLoop fix) the headless NE round-trip COMPLETED without the loadFromPreferences wedge. Running --headless disable via LaunchServices returned {"action":"disable","ok":true,"state":"disabled","build":{"git_sha":"a71cd190571f"}}, and the com.apple.networkextension log showed a clean session teardown (“configuration is now disabled, stopping” through “disconnected, last stop reason Configuration was disabled”). No 30s loadFromPreferences timed out.

Caveat: this exercised the DISABLE path against an ALREADY-SETTLED provider, which addresses the H2 (callback-to-parked-main-thread) hypothesis but NOT the H1 (first-arm-against-just-activated-provider registration stall) hypothesis that the W7-1 wedge was originally observed under. So this is evidence the RunLoop fix lets the headless NE completion handler fire, but it is NOT proof that the documented first-arm wedge is cleared. A first-arm drill (fresh sysext activation, Erik present, fortress provisioned) is still required to close W7-1.

Machine final state (Mini1, left safe)

Coordinator verification and claim movement

HALT. Do NOT merge #621, #633, or the combined branch from this thread. Do NOT edit ASSURANCE_MATRIX.md, README, or any public copy. The coordinator independently verifies this evidence (operator verdicts are provisional), then: