Skip to content

Development setup

This page is the shortest path from a clean checkout to a running Labstream build.

Requirements

  • macOS with Xcode and the visionOS SDK installed for the Labstream target.
  • The iOS/iPadOS 26.1+ SDK/runtime for the LabstreamMobile target.
  • The tvOS 26+ SDK and an installed tvOS simulator runtime for the LabstreamTV target.
  • macOS 26 on an Apple-silicon host when testing the LabstreamMac target.
  • A visionOS 26 or newer Apple Vision Pro simulator runtime compatible with the active Xcode.
  • Swift Package Manager for PMSKit tests (included with Xcode).
  • Python 3.11+ and uv for the repository's documentation and Python tooling checks.

Bootstrap the first visionOS simulator

Simulator IDs are machine-local and gitignored. A brand-new main checkout therefore has no .simid, and scripts/worktree-sim.sh setup cannot create the main worktree's initial visionOS simulator by itself. From the repository root, run this bootstrap once. It selects a usable existing simulator, preferring the newest compatible installed visionOS runtime, or creates a dedicated Labstream Golden simulator on the newest compatible runtime when none is available. It then validates the selected UDID and records it as the main worktree's golden simulator:

if [ ! -s .simid ]; then
  SIMID=$(xcrun simctl list devices available -j | python3 -c '
import json, re, sys
data = json.load(sys.stdin).get("devices", {})
def version(runtime):
    return tuple(int(n) for n in re.findall(r"\d+", runtime))
for runtime in sorted(data, key=version, reverse=True):
    if ".SimRuntime.xrOS-" not in runtime or version(runtime) < (26, 0):
        continue
    devices = data[runtime]
    preferred = [d for d in devices if d.get("name") == "Labstream Golden"]
    standard = [d for d in devices if d.get("name", "").startswith("Apple Vision Pro")]
    other = [d for d in devices if not d.get("name", "").startswith("vpwt-")]
    for device in preferred + standard + other:
        if device.get("isAvailable", True):
            print(device["udid"])
            raise SystemExit(0)
')

  if [ -z "$SIMID" ]; then
    RUNTIME=$(xcrun simctl list runtimes -j | python3 -c '
import json, re, sys
def version(runtime):
    return tuple(int(n) for n in re.findall(r"\d+", runtime.get("version", runtime.get("name", ""))))
runtimes = [r for r in json.load(sys.stdin).get("runtimes", [])
            if r.get("isAvailable", True)
            and ".SimRuntime.xrOS-" in r.get("identifier", "")
            and version(r) >= (26, 0)]
runtimes.sort(key=version, reverse=True)
if runtimes:
    print(runtimes[0]["identifier"])
')
    [ -n "$RUNTIME" ] || { printf '%s\n' "No compatible visionOS 26+ simulator runtime is installed." >&2; exit 1; }
    DEVICE_TYPES_FILE=$(mktemp "${TMPDIR:-/tmp}/labstream-device-types.XXXXXX") || exit 1
    xcrun simctl list devicetypes -j | python3 -c '
import json, sys
for device in json.load(sys.stdin).get("devicetypes", []):
    if device.get("isAvailable", True) and device.get("name", "").startswith("Apple Vision Pro"):
        print(device["identifier"])
' > "$DEVICE_TYPES_FILE"
    [ -s "$DEVICE_TYPES_FILE" ] || {
      rm -f "$DEVICE_TYPES_FILE"
      printf '%s\n' "No Apple Vision Pro simulator device type is installed." >&2
      exit 1
    }
    SIMID=""
    while IFS= read -r DEVICE_TYPE; do
      if SIMID=$(xcrun simctl create "Labstream Golden" "$DEVICE_TYPE" "$RUNTIME" 2>/dev/null); then
        break
      fi
      SIMID=""
    done < "$DEVICE_TYPES_FILE"
    rm -f "$DEVICE_TYPES_FILE"
    [ -n "$SIMID" ] || { printf '%s\n' "No Apple Vision Pro device type supports $RUNTIME." >&2; exit 1; }
  fi

  printf '%s\n' "$SIMID" > .simid
fi

SIMID=$(tr -d '[:space:]' < .simid)
xcrun simctl list devices -j | python3 -c '
import json, re, sys
udid = sys.argv[1]
def version(runtime):
    return tuple(int(n) for n in re.findall(r"\d+", runtime))
for runtime, devices in json.load(sys.stdin).get("devices", {}).items():
    if ".SimRuntime.xrOS-" in runtime and version(runtime) >= (26, 0):
        for device in devices:
            if device.get("udid") == udid and device.get("isAvailable", True):
                raise SystemExit(0)
raise SystemExit(f".simid does not name an available visionOS 26+ simulator: {udid}")
' "$SIMID"

scripts/worktree-sim.sh --platform visionos setup

If the bootstrap reports that no compatible runtime or device type is installed, add a visionOS 26+ Simulator runtime in Xcode Settings, then rerun it. If validation rejects a stale .simid, remove that file and rerun the bootstrap. A newly created simulator has no Labstream credentials; sign in through the app after the first launch when your test requires backend data. Do not commit .simid.

After the main checkout is initialized, scripts/worktree-sim.sh owns the lifecycle. A linked worktree gets a shutdown clone of the main simulator when you run:

scripts/worktree-sim.sh --platform visionos setup

Always resolve a concrete UDID with the script rather than targeting booted. Booted simulators are not free: obtain the current simulator lease, use only one simulator at a time, shut it down before switching platforms, and release the lease before another agent boots a simulator. Do not leave the golden visionOS simulator booted while a linked-worktree simulator is running.

Build for the visionOS simulator

Use a dedicated DerivedData directory and remove it before a verification build. That makes the product named by $APP unambiguous and ensures the subsequent install uses this build rather than another checkout's product:

scripts/worktree-sim.sh --platform visionos setup
SIMID=$(scripts/worktree-sim.sh --platform visionos id)
xcrun simctl boot "$SIMID" 2>/dev/null || true
DD="$PWD/build/DerivedData-visionos"
rm -rf "$DD"

scripts/xcodebuild-versioned.sh \
  -project Labstream.xcodeproj \
  -scheme Labstream \
  -destination "platform=visionOS Simulator,id=$SIMID" \
  -configuration Debug \
  -derivedDataPath "$DD" \
  build CODE_SIGNING_ALLOWED=NO

APP="$DD/Build/Products/Debug-xrsimulator/Labstream.app"
test -x "$APP/Labstream"

Continue with Install and observe a simulator smoke.

Build for an iPhone or iPad simulator

The mobile target is named/schemed LabstreamMobile and builds one universal iPhone/iPad app whose displayed product name is still Labstream. Unlike visionOS, the script can create a fresh iPhone or iPad simulator without a pre-existing golden simulator. Select the platform explicitly; a .simplatform file is optional convenience, not a prerequisite:

PLATFORM=iphone                    # change to ipad for the regular-width path
scripts/worktree-sim.sh --platform "$PLATFORM" setup
SIMID=$(scripts/worktree-sim.sh --platform "$PLATFORM" id)
xcrun simctl boot "$SIMID" 2>/dev/null || true
DD="$PWD/build/DerivedData-ios-$PLATFORM"
rm -rf "$DD"

scripts/xcodebuild-versioned.sh \
  -project Labstream.xcodeproj \
  -scheme LabstreamMobile \
  -destination "platform=iOS Simulator,id=$SIMID" \
  -configuration Debug \
  -derivedDataPath "$DD" \
  build CODE_SIGNING_ALLOWED=NO

APP="$DD/Build/Products/Debug-iphonesimulator/Labstream.app"
test -x "$APP/Labstream"

If Xcode says the iOS platform/runtime is missing or warns that the deployment target is newer than the installed SDK, install the matching iOS Simulator runtime/platform in Xcode Settings. A newer simulator runtime may not be usable with an older installed iOS SDK.

All four production targets use org.labstream.Labstream on their respective platforms; ordinary Mac host development is the documented exception and uses an isolated worktree identity. A development install can replace another Labstream build with the same bundle identifier on the same compatible device, but separate simulator devices and operating-system platforms do not share or replace one another's app container or login state.

Debug mobile builds also accept the credential-free browse fixture described in the tvOS section below. For a bounded agent smoke that builds, installs, launches, records screenshots/video/logs, and shuts down the leased simulator, use:

scripts/agent-sim-run.sh launch-fixture-home-passive   # visionOS named runner
scripts/agent-mobile-run.sh iphone fixture-home-passive --allow-simulator
# Use `ipad` to exercise the regular-width layout.

The mobile runner requires --allow-simulator as an assertion that its caller owns the repository's single-simulator lease. The visionOS runner has no such flag, so its caller must still hold the lease before invocation. VisionOS evidence defaults to artifacts/agent-sim-runs/; mobile and tvOS runner evidence defaults to artifacts/agent-platform-runs/. The fixture exposes stable accessibility targets including labstream.fixture.browse.root and labstream.home.fixture-resume.<backend>-orbit, so Xcode 27 Device Interaction can inspect and drive it semantically rather than by free-form coordinates.

Build for an Apple TV simulator

The tvOS release-candidate target is named/schemed LabstreamTV. It supports only the current Apple TV 4K third-generation simulator types; the helper intentionally does not fall back to older Apple TV hardware. Unlike visionOS, tvOS creates a fresh shutdown simulator rather than cloning the Vision Pro golden simulator:

scripts/worktree-sim.sh --platform tvos setup
SIMID=$(scripts/worktree-sim.sh --platform tvos id)
xcrun simctl boot "$SIMID" 2>/dev/null || true
DD="$PWD/build/DerivedData-tvos"
rm -rf "$DD"

scripts/xcodebuild-versioned.sh \
  -project Labstream.xcodeproj \
  -scheme LabstreamTV \
  -destination "platform=tvOS Simulator,id=$SIMID" \
  -configuration Debug \
  -derivedDataPath "$DD" \
  build CODE_SIGNING_ALLOWED=NO

APP="$DD/Build/Products/Debug-appletvsimulator/Labstream.app"
test -x "$APP/Labstream"

If worktree-sim.sh reports no available tvOS simulator runtime found, install the matching tvOS runtime in Xcode Settings. The SDK alone can still prove the compile foundation with a generic destination, but it cannot satisfy the launch/smoke gate:

scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj -scheme LabstreamTV \
  -destination 'generic/platform=tvOS Simulator' \
  -derivedDataPath "$PWD/build/DerivedData-tvos-generic" \
  build CODE_SIGNING_ALLOWED=NO

Continue with Install and observe a simulator smoke only when a concrete tvOS simulator runtime and $SIMID are available. The tvOS product deliberately has no downloads, Offline destination, or download-storage settings.

Debug builds on every platform expose production-isolated launch arguments for deterministic UI work. They never read or persist production credentials: --ui-testing starts signed out, --ui-testing-backend plex|jellyfin|emby selects the authentication surface, and --ui-testing-fixture browse supplies synthetic data to the real Home/Libraries/Detail views:

xcrun simctl launch "$SIMID" org.labstream.Labstream \
  --ui-testing --ui-testing-backend plex --ui-testing-fixture browse

Install and observe a simulator smoke

After either simulator build above, $SIMID and $APP identify the exact simulator and product. Install that product, compare the installed executable with it, launch, inspect bounded logs, and capture a screenshot:

xcrun simctl install "$SIMID" "$APP"
INSTALLED_APP=$(xcrun simctl get_app_container "$SIMID" org.labstream.Labstream app)
BUILT_UUID=$(xcrun dwarfdump --uuid "$APP/Labstream" | cut -d' ' -f2)
INSTALLED_UUID=$(xcrun dwarfdump --uuid "$INSTALLED_APP/Labstream" | cut -d' ' -f2)
[ "$BUILT_UUID" = "$INSTALLED_UUID" ] || {
  printf '%s\n' "Installed executable does not match $APP" >&2
  exit 1
}
printf '%s\n' "UUID_MATCH $BUILT_UUID"

xcrun simctl terminate "$SIMID" org.labstream.Labstream 2>/dev/null || true
xcrun simctl launch "$SIMID" org.labstream.Labstream
sleep 3
xcrun simctl spawn "$SIMID" log show --last 2m \
  --predicate 'process == "Labstream"' | tail -120
mkdir -p build
xcrun simctl io "$SIMID" screenshot build/labstream-smoke.png

A passing smoke is observable, not just a successful build: simctl launch prints a process ID; the bounded log has no crash, fatalError, or assertion for the changed area; and build/labstream-smoke.png shows the expected app surface. A fresh simulator normally shows sign-in, while a previously configured simulator should reach its browse UI. Inspect the PNG locally and do not publish screenshots that expose server or media details.

Shut the simulator down as soon as the check finishes:

xcrun simctl shutdown "$SIMID"

# Credential-free semantic mobile evidence (run one platform at a time under the lease).
scripts/agent-mobile-run.sh iphone fixture-detail-semantic --allow-simulator
scripts/agent-mobile-run.sh ipad fixture-detail-semantic --allow-simulator

Shutting down preserves the simulator and its app state. Do not use teardown for the main worktree's golden visionOS simulator.

Linked-worktree simulator cleanup

Linked worktrees own their vpwt-*, iphonewt-*, ipadwt-*, and tvwt-* simulators. Before removing a linked worktree, delete all of its simulators and then remove the worktree:

scripts/worktree-sim.sh closeout /path/to/linked-worktree
git worktree remove /path/to/linked-worktree

closeout can be run from any remaining checkout of the repository. It tears down simulator state for an existing linked worktree and prunes orphaned Labstream worktree simulators. If a worktree was already removed, run scripts/worktree-sim.sh prune as the cleanup backstop. Neither command deletes the main worktree's golden simulator.

Build and run the macOS target

The LabstreamMac target runs directly on the Apple-silicon host; there is no Mac simulator lane. Use the host helper so builds are staged under a per-worktree development identity:

scripts/deploy-macos-to-host.sh --launch

For a credential-free semantic agent check, the bounded runner stages an isolated bundle, mounts the real browse/detail UI with synthetic data, presses the stable home-item Accessibility target, asserts the detail tagline, captures only the Labstream window, and stops its exact process:

scripts/agent-macos-run.sh fixture-detail

Evidence is written beneath ignored artifacts/agent-platform-runs/. Exit code 2 means a host precondition such as Accessibility or Screen Recording permission is unavailable; the runner does not fall back to coordinates or full-desktop capture.

Credential-free App Store capture composes these platform runners into a serial, exact-worktree-ID workflow and validates Apple's accepted dimensions, alpha prohibition, and checksums. See App Store screenshot automation. On Xcode 27 the visionOS lane is passive Home capture only; richer visionOS marketing states remain a human/headset gate.

The Mac target is a pre-release App Store candidate, not a released compatibility promise. Routine host builds still use per-worktree identities to isolate containers and credentials. See the macOS target guide for identity isolation, cleanup, validation, and release gates.

Core validation commands

# Hermetic Swift package correctness; live probes remain separate and opt-in
swift test --package-path PMSKit --no-parallel --skip 'Live.*ProbeTests'

# Repository hygiene, redaction, and tooling tests
scripts/ci-hygiene.sh

# Documentation build
uv run --with-requirements requirements.txt mkdocs build --strict

PMSKit/Tests/PMSKitTests is the portable package suite. App-owned deterministic tests live in LabstreamTests/ and are hosted by platform-specific Xcode test targets over the same test sources:

  • LabstreamTests, selected by the LabstreamMobile scheme and LabstreamTests.xctestplan, runs on an iPhone/iPad simulator;
  • LabstreamMobileUITests, selected by the same scheme/test plan, owns the credential-free semantic Home-to-detail regression used by the mobile agent loop;
  • LabstreamMacTests, selected by the LabstreamMac scheme and LabstreamMacTests.xctestplan, runs on the macOS host;
  • LabstreamTVTests, selected by LabstreamTVTests.xctestplan, runs the applicable shared app tests for tvOS while excluding the approved download-only exception; LabstreamTVUITests.xctestplan isolates deterministic authentication and the initial remote-only Home-to-detail browse journey so unrelated hosted sources cannot block UI execution.

Run the app suite for the platform affected by a change (all three hosts — iOS, macOS, and tvOS — for shared app infrastructure):

For a deterministic smoke, affected-platform, or full plan, use the checked native-test-matrix.py driver. The commands below remain the direct forms for an individually leased platform lane.

# Mobile-hosted app tests.
scripts/worktree-sim.sh --platform iphone setup
SIMID=$(scripts/worktree-sim.sh --platform iphone id)
xcrun simctl boot "$SIMID" 2>/dev/null || true
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
  -scheme LabstreamMobile -testPlan LabstreamTests \
  -destination "platform=iOS Simulator,id=$SIMID" test CODE_SIGNING_ALLOWED=NO
xcrun simctl shutdown "$SIMID"

# Mac-hosted app tests.
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
  -scheme LabstreamMac -testPlan LabstreamMacTests \
  -destination 'platform=macOS,arch=arm64' test CODE_SIGNING_ALLOWED=NO

# tvOS test build (works with the SDK even before a runtime is installed).
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
  -scheme LabstreamTV -testPlan LabstreamTVTests \
  -destination 'generic/platform=tvOS Simulator' build-for-testing CODE_SIGNING_ALLOWED=NO

# tvOS unit and UI suites (requires the worktree's concrete tvOS simulator).
scripts/worktree-sim.sh --platform tvos setup
SIMID=$(scripts/worktree-sim.sh --platform tvos id)
xcrun simctl boot "$SIMID" 2>/dev/null || true
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
  -scheme LabstreamTV -testPlan LabstreamTVTests \
  -destination "platform=tvOS Simulator,id=$SIMID" \
  -only-testing:LabstreamTVTests test CODE_SIGNING_ALLOWED=NO -enableCodeCoverage NO
scripts/xcodebuild-versioned.sh -project Labstream.xcodeproj \
  -scheme LabstreamTV -testPlan LabstreamTVUITests \
  -destination "platform=tvOS Simulator,id=$SIMID" \
  -only-testing:LabstreamTVUITests test CODE_SIGNING_ALLOWED=NO -enableCodeCoverage NO

# Credential-free TV evidence wrappers.
scripts/agent-tvos-run.sh fixture-home-semantic --allow-simulator
scripts/agent-tvos-run.sh fixture-player-basic --allow-simulator
xcrun simctl shutdown "$SIMID"

The shared app suites are host-app unit tests, not live-server acceptance tests. Keep policy and wire-format logic in PMSKit tests; use the app suites for app-owned persistence/filesystem, credential adapters, lifecycle coordination, playback ownership, and other platform integration seams. Running the tvOS tests and UI launch harness requires a concrete tvOS simulator runtime.

Physical Apple Vision Pro install

Simulator builds are unsigned and cannot install on hardware. Complete Apple's one-time device and signing setup before using the repository wrapper:

  1. Update the headset and Xcode to mutually compatible visionOS versions.
  2. On Apple Vision Pro, enable Developer Mode in Settings > Privacy & Security > Developer Mode and restart the headset if prompted.
  3. Keep the Mac and headset on the same Wi-Fi, then open Xcode > Window > Devices and Simulators. Select the discovered Apple Vision Pro, choose Pair, and enter or confirm the pairing code on the headset. Accept any trust prompt. The headset must be awake and worn for discovery, install, and launch.
  4. In Xcode > Settings > Accounts, sign in with the Apple ID that belongs to the development team. Allow Xcode to create an Apple Development certificate if the account has none.
  5. Confirm that CoreDevice can see an available headset:
xcrun devicectl list devices

Use the wrapper rather than re-deriving device destinations, signing-team IDs, provisioning, build locations, or install commands:

scripts/deploy-to-device.sh            # signed Debug-xros build + install
scripts/deploy-to-device.sh --launch   # also launch while the headset is awake/worn
scripts/deploy-to-device.sh --no-build # reinstall the last Debug-xros build
scripts/deploy-to-device.sh --verbose  # show full device/team IDs for troubleshooting

If several visionOS devices are paired, select one with VP_DEVICE_ID=<device-uuid> scripts/deploy-to-device.sh --launch. The script normally derives the development team from the Apple Development certificate; VP_DEVELOPMENT_TEAM=<team-id> is the explicit override.

A certificate visible to security find-identity -p codesigning -v is not sufficient by itself for command-line automatic provisioning. If the build reports No Account for Team or that no profile for org.labstream.Labstream was found, sign the matching Apple ID into Xcode Settings and rerun the script. For the first install, opening the project in Xcode, choosing the paired headset, and running the Labstream scheme once is also a valid way to let Xcode finish interactive registration and provisioning.

An unavailable headset usually needs to be woken, worn, and returned to the same Wi-Fi as the Mac. Developer Mode and trust prompts must be completed on the headset; the deploy script cannot perform those steps.

The development build uses the same bundle identifier as the intended App Store identity, so a local install can replace another installed build and its app state. Development provisioning profiles can also expire; review the profile lifetime printed by the wrapper before relying on an offline install.

For an install that must outlive a development provisioning profile (for example, offline use away from the development Mac), use the distribution-signed Ad Hoc wrapper instead. It requires an Apple Distribution certificate and a non-expired Ad Hoc provisioning profile that includes the headset's UDID:

scripts/deploy-ad-hoc-to-device.sh                 # distribution-signed build + install
scripts/deploy-ad-hoc-to-device.sh --launch        # also launch
scripts/deploy-ad-hoc-to-device.sh --list-profiles # inspect installed Ad Hoc profiles
scripts/deploy-ad-hoc-to-device.sh --profile "<name-or-uuid>"

Select a device or team explicitly with VP_DEVICE_ID, VP_DISTRIBUTION_TEAM, or VP_AD_HOC_PROFILE_SPECIFIER when the defaults are ambiguous.

Physical iPhone or iPad install

Use the mobile wrapper for a signed iphoneos build:

scripts/deploy-mobile-to-device.sh
scripts/deploy-mobile-to-device.sh --launch
scripts/deploy-mobile-to-device.sh --no-build

If more than one phone or tablet is paired, set IOS_DEVICE_ID=<device-uuid> explicitly. First use still requires pairing/trust, Developer Mode, and the matching Apple ID in Xcode Settings.

Credentials and iCloud Keychain sync

The app persists its long-lived secrets in the Keychain (Labstream/Shared/Auth/KeychainStore.swift). Exactly one item is stored as an iCloud-synchronizable Keychain item: the Plex account token. Because the canonical visionOS, iPhone/iPad, tvOS, and Mac variants share the org.labstream.Labstream bundle id and Keychain service string, a Plex sign-in can synchronize to another device through iCloud Keychain when the platform and the user's Keychain settings permit. The normal per-worktree Mac development build deliberately uses isolated, backup-excluded credential storage instead; see the macOS target guide.

Everything else is deliberately device-local:

  • Plex clientIdentifier — generated once per install and never synced. Combined with a distinct X-Plex-Device-Name (see Labstream/Shared/App/PlatformClientIdentity.swift), every device presents a unique X-Plex-Client-Identifier, so the server still sees truly independent, per-device-identifiable sessions even though the token is shared. Syncing it would merge all devices into one server-side client identity, breaking per-device session listings and transcode bookkeeping.
  • Jellyfin/Emby access tokens — those servers mint the access token bound to the device id presented at authentication (token and device are one server-side record). Syncing the token would make every physical device impersonate a single server-side device, causing session collisions, merged played-on attribution, and broken remote-control targeting. Jellyfin/Emby therefore still require a per-device sign-in; Quick Connect / Emby Connect keeps that to a short-code step.
  • Backend/server selection — a per-device preference, not a credential.

Because the Plex token is the shared item, deleting it — a manual sign-out or a 401-triggered wipe — propagates sign-out to all devices, which matches how an account-level token actually dies. When a synced Plex token is successfully read, the app deletes any pre-sync device-local Plex token so a later synced/global sign-out cannot re-promote stale local credentials.

Caveat for the simulator: simulator builds use CODE_SIGNING_ALLOWED=NO and cannot access the real Keychain, so KeychainStore falls back to a file store. iCloud sync therefore only manifests on real devices with iCloud Keychain enabled; you cannot observe cross-device sign-in in the simulator.

The type doc comment at the top of KeychainStore.swift is the source of truth for this behavior; keep it and this section in agreement.

Logs

SIMID=$(scripts/worktree-sim.sh --platform visionos id)
xcrun simctl spawn "$SIMID" log show --last 10m --info --debug \
  --predicate 'subsystem == "org.labstream.Labstream"'

Verified platform findings

  • visionOS wake silently restarts custom-Range request bodies, and resume data cannot see it. When a headset is re-worn, the network path re-evaluates and nsurlsessiond transparently retries the in-flight background task; because Labstream's static-range downloads carry a custom Range header, the retried body restarts from the range start with no error and no resume-data callback — the failure is invisible to the resume-data recovery path entirely. Observed signature: an app-diagnostics reset_body_bytes on the order of ~1 KB (i.e. the retried body barely got going again) even though gigabytes had already been buffered un-appended for that task. Contrast with an app-alive network switch on iPad, which surfaces as a normal task error WITH resume data and is recoverable through the existing resume-data path. Consequence: off-head durability for static-range downloads cannot rely on resume data alone, and per-chunk background wakes to checkpoint more often are not viable either — the OS background-relaunch rate limiter (exponential backoff, #212) stops granting wakes once a design needs one wake per bounded transfer, stalling overnight. The fix is a pre-queued train of closed-range segment tasks that nsurlsessiond executes without app involvement, bounding what a silent wake-time retry can destroy to one segment; see docs/DOWNLOADS-OFFLINE.md for the design.

Documentation workflow

Preview the published site locally with:

uv run --with-requirements requirements.txt mkdocs serve

Use the strict documentation build in Core validation commands before publishing changes. Keep current product, architecture, and contributor guidance in the published Markdown files at the top of docs/. Classify repository-internal documents into these unpublished lanes:

  • docs/plans/ for active implementation plans and acceptance journals;
  • docs/research/ for unresolved investigations;
  • docs/evidence/ for immutable audit and profiling observations; and
  • docs/archive/ for completed, superseded, or closed context that is never canonical.

The current manual validation matrix deliberately remains at the repository root in TESTING-CHECKLIST.md. Each lane README defines its naming and promotion/archive rules. Preserve historical prose when moving snapshots, but repair live links, navigation, includes, and script references.