From 031dc50b99cae4b072bb87c7606a081d06f73c19 Mon Sep 17 00:00:00 2001
From: Mikei386 <44135113+Mikei386@users.noreply.github.com>
Date: Sun, 13 Sep 2026 10:33:13 +0200
Subject: [PATCH] Validate exact Vision boundary values and real-project
regression
---
Resources/Info.plist | 4 +--
Sources/GrowthLapse/VideoProcessor.swift | 7 +++--
Tests/Integration/RenderIntegration.swift | 35 +++++++++++++++++++++--
3 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/Resources/Info.plist b/Resources/Info.plist
index 70e6261..36fa050 100644
--- a/Resources/Info.plist
+++ b/Resources/Info.plist
@@ -19,9 +19,9 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 0.4.1
+ 0.4.2
CFBundleVersion
- 6
+ 7
LSMinimumSystemVersion
13.0
NSHighResolutionCapable
diff --git a/Sources/GrowthLapse/VideoProcessor.swift b/Sources/GrowthLapse/VideoProcessor.swift
index c2f8c9d..1751d57 100644
--- a/Sources/GrowthLapse/VideoProcessor.swift
+++ b/Sources/GrowthLapse/VideoProcessor.swift
@@ -924,9 +924,10 @@ final class VideoProcessor: ObservableObject {
if let pose = sample.pose {
guard [pose.x, pose.y, pose.height, pose.yaw, pose.roll].allSatisfy({ $0.isFinite }),
(0...1).contains(pose.x), (0...1).contains(pose.y), (0...1).contains(pose.height),
- // Vision angles originate as Float; Float.pi is slightly larger than Double.pi.
- abs(pose.yaw) <= Double(Float.pi), abs(pose.roll) <= Double(Float.pi),
- pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= Double(Float.pi)) else { return false }
+ // Vision may return either Float rounding adjacent to pi. Swift Float.pi
+ // is not necessarily the rounding used by Vision; allow 1e-6 radians.
+ abs(pose.yaw) <= (.pi + 1e-6), abs(pose.roll) <= (.pi + 1e-6),
+ pose.pitch == nil || (pose.pitch!.isFinite && abs(pose.pitch!) <= (.pi + 1e-6)) else { return false }
}
if let color = sample.color {
guard color.luminance.isFinite, (0...1).contains(color.luminance),
diff --git a/Tests/Integration/RenderIntegration.swift b/Tests/Integration/RenderIntegration.swift
index bb70dce..6111d2a 100644
--- a/Tests/Integration/RenderIntegration.swift
+++ b/Tests/Integration/RenderIntegration.swift
@@ -29,6 +29,35 @@ struct RenderIntegration {
@MainActor
static func main() async throws {
+ if let path = ProcessInfo.processInfo.environment["GROWTHLAPSE_VALIDATE_PROJECT"] {
+ let validator = VideoProcessor()
+ guard let project = validator.loadProject(from: URL(fileURLWithPath: path)) else {
+ throw TestFailure.failed("Real project validation failed: \(validator.state)")
+ }
+ print("PASS real project load: \(project.clips.count) clips")
+ if ProcessInfo.processInfo.environment["GROWTHLAPSE_TEST_REAL_DRAFT"] == "1" {
+ let original = try Data(contentsOf: URL(fileURLWithPath: path))
+ var subset = project
+ subset.clips = project.clips.filter { $0.displayName.hasPrefix("063") || $0.displayName.hasPrefix("066") }
+ try require(subset.clips.count == 2, "Expected two selected regression clips")
+ validator.project = subset
+ validator.startDraft(settings: SettingsStore.load())
+ let deadline = Date().addingTimeInterval(180)
+ while validator.state.isRunning && Date() < deadline {
+ try await Task.sleep(nanoseconds: 100_000_000)
+ }
+ guard let draft = validator.draftOutput else {
+ validator.cancel()
+ throw TestFailure.failed("Real draft failed: \(validator.state)\n\(validator.logs.suffix(25).joined(separator: "\n"))")
+ }
+ try require(FileManager.default.fileExists(atPath: draft.path), "Real draft missing")
+ let after = try Data(contentsOf: URL(fileURLWithPath: path))
+ try require(original == after, "Real draft changed source project")
+ print("PASS actual affected clips rendered as isolated draft: \(draft.path)")
+ validator.discardDraft()
+ return
+ }
+ }
try ContinuityChecks.run()
let defaults = RenderSettings()
try require(defaults.inputFolder == nil && defaults.outputFile == nil
@@ -198,7 +227,7 @@ struct RenderIntegration {
// Vision's single-precision ±pi must survive project validation unchanged.
var angleProject = second
- let visionPi = Double(Float.pi)
+ let visionPi = 3.1415927410125732 // Exact value observed in Vision JSON; not Swift Float.pi
angleProject.clips[0].visualAnalysis = ClipVisualAnalysis(sourceSignature: "angle-test", samples: [
VisualSample(time: 0, faceCount: 1,
pose: FacePose(x: 0.5, y: 0.5, height: 0.3, yaw: visionPi,
@@ -210,10 +239,10 @@ struct RenderIntegration {
let accepted = angleLoader.loadProject(from: angleURL)
try require(accepted?.clips[0].visualAnalysis == angleProject.clips[0].visualAnalysis,
"Valid Vision Float.pi angles rejected or altered")
- angleProject.clips[0].visualAnalysis!.samples[0].pose!.roll = 3.15
+ angleProject.clips[0].visualAnalysis!.samples[0].pose!.roll = Double.pi + 0.00001
try JSONEncoder().encode(angleProject).write(to: angleURL)
try require(angleLoader.loadProject(from: angleURL) == nil, "Out-of-range angle accepted")
- print("PASS Vision Float.pi project round-trip and invalid-angle rejection")
+ print("PASS Vision exact boundary project round-trip and invalid-angle rejection")
// Exercise failure after normalization, while FFmpeg is producing the final video.
let wrapper = root.appendingPathComponent("ffmpeg-fail")