bpy.context not working as expected
Thanks to iceythe on BlenderArtists I have resolved my problems in two ways. The first part is this:

for window in context.window_manager.windows:
    screen = window.screen

    for area in screen.areas:
        if area.type == 'VIEW_3D':

            tmp = bpy.context.copy()
            tmp.update(window=window, screen=screen, area=area)

            class Override:
                pass

            for key, val in tmp.items():
                setattr(Override, key, val)
            print('Unwrapping ...')
            from bl_operators.uvcalc_smart_project import main as uv_smart_project
            uv_smart_project(Override, .02, 30, 0.03, True, True)

            return 


The second part was a problem caused by my using save/reopen to clear orphaned data blocks, whereas this avoids the problem by using the purge function (which I knew about but had struggled to implement via a script):

context = bpy.context

def override():
    for window in context.window_manager.windows:
        for area in window.screen.areas:
            if area.type == 'OUTLINER':
                override_dict = {}
                override_dict.update(area=area)
                return override_dict

def ClearAll():

    for c in context.scene.collection.children:
        context.scene.collection.children.unlink(c)

    for c in bpy.data.collections:
        if not c.users:
            bpy.data.collections.remove(c)

    for o in bpy.data.objects:
        try:
            bpy.data.objects.remove(o)
        except:
            print('All objects deleted')

    override_dict = override()
    if override_dict is not None:
        bpy.ops.outliner.orphans_purge(override_dict)