ReviewOS

craft-native/craft

~60 gated dispatcher arms have no else — a disabled capability hangs the page's promise forever

#116
Closed glennmichael123 opened this 10 days ago ·
10 days ago

Most gated arms in the spec's dispatcher are shaped like this:

case "registerBackgroundTask":
    if config.enableBackgroundTasks,
       let taskId = body["taskId"] as? String {
        registerBackgroundTask(taskId: taskId, callbackId: callbackId)
    }
    // no else

enableBackgroundTasks defaults to false (CraftApp.swift:219). With the gate off, no arm runs, no callback is ever reached, and the page's promise never settles — these wrappers do not all carry the 30-second timeout, so it is not a slow rejection, it is silence.

An earlier audit put the count at roughly 60 of 65 gated cases. I verified registerBackgroundTask concretely; the shape is visibly the same across the file.

Why it mostly hides

For actions Zig serves, ios_config.gateFor refuses with CAPABILITY_DISABLED before the spec's arm is ever reached, so the page gets a rejection it can catch. The hang is only reachable for actions that fall through to the shim — and only in an app built without the Zig runtime linked, which is the shipping default today.

bridge_mobile_bgtasks.zig anticipated exactly this and wrote down a trigger condition:

if that fix does not land, reconsider this omission

The fix did not land.

The options, none of them free

  1. Add else { rejectCallback(…, code: "CAPABILITY_DISABLED") } to the gated arms. Correct and matches what Zig already answers for its own actions. It is ~60 arms of spec, and the spec is what the conformance test reads.
  2. Claim the affected actions in Zig as .status = .unavailable. They then dispatch and refuse. But .unavailable takes an action away from a shim that might serve it correctly when the gate is on — right for registerBackgroundTask, wrong in general.
  3. Leave it. A page calling a disabled capability hangs forever with no diagnostic.

Worth doing 1 as one sweep rather than piecemeal — fixing four arms because they happen to be in the module someone is reading is how a systemic bug becomes a scattered one.

Related: #115 (a different silent-failure shape in the same file).

Sign in to comment on this issue.