Closes #260.
The flake was a real location bug
The five ids the slice job waited on aren't five hung calls. They're one chain of markers page.m sends once each answer arrives:
getCurrentPosition(i=110) answers with coordinates, and the page sends marker 76.- The cached request (i=111) answers, and it sends marker 78.
- The zero-timeout request (i=112) times out, and it sends marker 82.
- Speech ends, and it sends marker 72.
openSettingsanswers, and it sends marker 54.
Both failing runs broke at the first link, in the same way:
info: craft-bridge dispatch t=mobile a=getCurrentPosition i=110
warning: location: CLLocationManager failed - domain=kCLErrorDomain code=0 …
FAIL: timed out after 900s waiting for: i=54 i=72 i=76 i=78 i=82Those are jobs 106721185520 and 106411898966. A passing run (35626485544) shows 110, 111 and 112 in order, with no error.
Code 0 is kCLErrorLocationUnknown, which Apple documents as the manager being unable to get a location right away, not as a failure. Both the Zig bridge and the Swift shim rejected the waiting getCurrentPosition on any Core Location error. So on a loaded simulator, where the first fix is slow, request 110 failed at once, and the chain after it never started. A phone with a cold GPS would fail the same request the same way. The late Siri completion in the same logs was only a symptom of the slow simulator.
Change
Both sides use requestLocation, which gives up on that error rather than retrying on its own, so ignoring the error would just wait out the timeout. Instead, on kCLErrorLocationUnknown:
The one-shot asks again after a second, but only if the same caller is still the one waiting:
- Zig checks the pending ticket's index and generation.
- Swift checks
singleLocationCallbackId.
A caller that's been answered, timed out or replaced stops the asking, so the caller's own timeout still bounds the whole wait.
No
craftLocationErrorevent. A watch usesstartUpdatingLocation, which does keep trying, so an error event would tell the page something that isn't true.Every other error behaves exactly as before.
In Zig, the handling moved from didFailWithError into handleLocationFailure(domain, code, description), so it can be tested without an NSError. The retry uses ios_async.scheduleDeadline's dispatch_after, the same timer the timeout uses.
Tests, and what they don't prove
Zig: a new host test covers the full sequence.
- A pending one-shot survives
kCLErrorDomain/0. - Only the same ticket counts as still waiting.
- A real failure (
kCLErrorDomain/2) still settles the call.
With the new branch disabled, it fails with
error.NoFixYetSettledTheCall.- A pending one-shot survives
Swift: an
index.test.tsassertion checks that the no-fix branch comes before anything that settles the caller or emits the event, and that the retry is guarded by the same callback id.typecheck-template.shpasses.No end-to-end reproduction. I tried to reproduce it on the simulator harness by clearing the location and setting it seconds after the page asked.
simctl location cleardoesn't leave the simulator without a fix: it falls back to a default location, and the shim leg got San Francisco immediately.simctlhas no way to put Core Location into "no fix yet", so I dropped that harness change rather than weaken the location case with it.The fix therefore rests on the diagnosis from the two CI logs and on the unit tests. If the slice job fails this way again, the log will now show
location: no fix yet (kCLErrorLocationUnknown); asking again in 1000 mslines, which would point past this cause.