commit 2156def908cb25c2e7968dfab91b5edb34292730
parent b1267fb6c240f6211a07f08b2ddb37d4dc0fc5fe
Author: triesap <tyson@radroots.org>
Date: Thu, 18 Jun 2026 12:55:45 -0700
runtime: make config and logging deterministic
- read release app config from plist-backed build values only
- keep launch environment overrides behind the Debug UI-test harness
- fail startup explicitly when runtime logging setup fails
- record logging initialization without fallback diagnostics
Diffstat:
4 files changed, 35 insertions(+), 20 deletions(-)
diff --git a/Radroots/Runtime/BuildConfig.swift b/Radroots/Runtime/BuildConfig.swift
@@ -16,12 +16,12 @@ enum BuildConfigKey: String {
enum BuildConfig {
static func string(_ key: BuildConfigKey) -> String? {
- envString(key) ?? infoString(key).map { stripOuterQuotes($0) }
+ debugLaunchOverrideString(key) ?? infoString(key).map { stripOuterQuotes($0) }
}
static func bool(_ key: BuildConfigKey) -> Bool? {
- if let env = ProcessInfo.processInfo.environment[key.rawValue],
- let parsed = parseBool(env) {
+ if let raw = debugLaunchOverrideString(key),
+ let parsed = parseBool(raw) {
return parsed
}
if let v = infoValue(for: key.rawValue) {
@@ -33,7 +33,7 @@ enum BuildConfig {
}
static func array(_ key: BuildConfigKey, splitBy set: CharacterSet = .whitespacesAndNewlines) -> [String]? {
- if let raw = envString(key) {
+ if let raw = debugLaunchOverrideString(key) {
return parseArray(raw, splitBy: set)
}
if let direct = infoArray(key) {
@@ -66,12 +66,6 @@ enum BuildConfig {
return out
}
- private static func envString(_ key: BuildConfigKey) -> String? {
- ProcessInfo.processInfo.environment[key.rawValue]
- .flatMap { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
- .flatMap { $0.isEmpty ? nil : stripOuterQuotes($0) }
- }
-
private static func parseArray(_ value: String, splitBy set: CharacterSet) -> [String]? {
var raw = value.trimmingCharacters(in: .whitespacesAndNewlines)
guard !raw.isEmpty else { return nil }
@@ -133,4 +127,18 @@ enum BuildConfig {
}
return s
}
+
+ #if DEBUG
+ private static func debugLaunchOverrideString(_ key: BuildConfigKey) -> String? {
+ guard FieldUITestHarness.isRequested else {
+ return nil
+ }
+ return FieldUITestHarness.string(key.rawValue)
+ .flatMap { $0.isEmpty ? nil : stripOuterQuotes($0) }
+ }
+ #else
+ private static func debugLaunchOverrideString(_ key: BuildConfigKey) -> String? {
+ nil
+ }
+ #endif
}
diff --git a/Radroots/Runtime/FieldTelemetry.swift b/Radroots/Runtime/FieldTelemetry.swift
@@ -77,18 +77,14 @@ final class FieldTelemetry: @unchecked Sendable {
await sink.record(event)
}
- func runtimeLoggingInitialized(
- settings: LoggingSettings,
- fallbackUsed: Bool
- ) {
+ func runtimeLoggingInitialized(settings: LoggingSettings) {
record(
name: "field_ios.runtime.logging_initialized",
- level: fallbackUsed ? .warning : .info,
+ level: .info,
fields: [
try? .bool("stdout_enabled", settings.stdout),
try? .bool("file_enabled", settings.fileEnabled),
try? .string("logging_filter", settings.level ?? "unset"),
- try? .bool("fallback_used", fallbackUsed)
].compactMap { $0 }
)
}
@@ -327,6 +323,8 @@ final class FieldTelemetry: @unchecked Sendable {
switch error {
case FieldUserPresenceGateError.notVerified:
return "unverified"
+ case is FieldRuntimeLoggingError:
+ return "logging_initialization_failed"
case let error as RadrootsUserPresenceError:
return userPresenceOutcome(for: error)
case let error as RadrootsCaptureIntakeError:
diff --git a/Radroots/Runtime/LoggingSettings.swift b/Radroots/Runtime/LoggingSettings.swift
@@ -1,5 +1,16 @@
import Foundation
+enum FieldRuntimeLoggingError: LocalizedError {
+ case initializationFailed(String)
+
+ var errorDescription: String? {
+ switch self {
+ case .initializationFailed(let message):
+ "Runtime logging initialization failed: \(message)"
+ }
+ }
+}
+
struct LoggingSettings: Equatable {
var stdout: Bool
var fileEnabled: Bool
diff --git a/Radroots/Runtime/Radroots.swift b/Radroots/Runtime/Radroots.swift
@@ -15,14 +15,12 @@ public final class Radroots: ObservableObject {
telemetry: FieldTelemetry = .shared
) throws -> FieldRuntimeService {
let settings = LoggingSettings.load()
- var loggingFallbackUsed = false
do {
try settings.apply(bundleIdentifier: bundleId)
} catch {
- try? initLoggingStdout()
- loggingFallbackUsed = true
+ throw FieldRuntimeLoggingError.initializationFailed(error.localizedDescription)
}
- telemetry.runtimeLoggingInitialized(settings: settings, fallbackUsed: loggingFallbackUsed)
+ telemetry.runtimeLoggingInitialized(settings: settings)
let rt = try RadrootsRuntime()
let resolvedSha = buildSha ?? (Bundle.main.object(forInfoDictionaryKey: "GIT_SHA") as? String)