Skip to content

calque spawn-run never ships sibling functions/constants/imports a spawned callable's body bare-references #198

Description

@scttfrdmn

Context

Found via a real calque spawn-run --bucket ... --ami ami-09b4d1958a23cbe9e --instance g6.2xlarge modal/forecasts_app.py --i-understand-this-spends-money run against real AWS (AI-Almanac's forecasts_app.py). The classified callable, run_season_forecast_bundle, has this real body:

def run_season_forecast_bundle(job_id, model_id, config, season_params):
    return _season_bundle_impl(job_id, model_id, config, season_params)

_season_bundle_impl is a plain module-level sibling function (not itself an @app.function). The real run acquired an instance, ran host-mode (no docker needed for this callable), and failed immediately:

LEAK[unhandled_case] item 0 failed in payload: name '_season_bundle_impl' is not defined

Root cause

cmd/calque/run.go's collectLocalExtras (transitive-closure resolution of sibling functions/constants/imports/classes a picked unit's body bare-references, calque#92/#139/#146/#147) is called by dryRunWarm and manifestBodyForUnit — the paths run --dry-run/real/fleetrun all go through. internal/exec/spawnshard.go's ResolveSpawnCallables/BuildSpawnManifests and cmd/calque/spawnrun.go's runSpawnShard never call it at all (confirmed by grep — zero references to Extras/collectLocalExtras anywhere in spawnshard.go or spawnrun.go). SpawnCallable.MethodBody is shipped completely verbatim with no sibling resolution whatsoever.

This is exactly the class of gap calque#92/#139/#146/#147 already closed for every OTHER real-run path — spawn-run was simply never updated to use the same mechanism when those were built.

Why this matters

This isn't a narrow edge case — a .spawn()'d callable delegating to a private helper function (keeping the @app.function-decorated wrapper thin) is a completely ordinary Python pattern, and it's exactly what broke here. Any real .spawn()-shaped script with this common structure will hit the identical NameError today.

Ask

runSpawnShard/spawnManifestBody (cmd/calque/spawnrun.go) should resolve and ship sibling functions/constants/imports/classes the SAME way manifestBodyForUnit already does for real/fleetrun — likely via the same collectLocalExtras call, given SpawnCallable's Key (the callable name) and the parsed ir.App. This needs SpawnCallable/ResolveSpawnCallables to either carry enough info for collectLocalExtras to run against it (it takes a warmUnit, calque's own internal shape — may need adapting or a parallel resolution path), or for spawnrun.go to call collectLocalExtras directly per-callable using the already-available ir.App.

Real repro: testdata/scripts/spawn_fanout.py (the existing calque#88 fixture) doesn't currently exercise this — its worker_a/worker_b bodies are self-contained. Worth adding a sibling-function-referencing spawn callable to that fixture (or a new one) to regression-test whichever fix lands, mirroring how testdata/scripts/plain_function.py/free_refs_*.py already cover this for the non-spawn paths.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions