Context
Found while writing an end-to-end regression test for calque#189 (TestSpawnDictDispatchEndToEndProducesRealShards, cmd/calque/spawn_dict_dispatch_e2e_test.go) — confirming calque spawn-run's pipeline actually produces a real shard, not just that call-site/classification resolve correctly.
internal/exec/spawnshard.go's SpawnCallable{MethodArg: f.ItemArg} (from ResolveSpawnCallables) only ever carries the callable's FIRST non-self/cls parameter name — same shape ir.Function.ItemArg uses everywhere else in this codebase (the single-arg .map()-style binding protocol). spawnArgsPayload (same file) packs a call site's multiple real args into a list when there's more than one, but nothing on the callable-definition side has a binding convention for more than one name — the function's own doc comment already says this explicitly:
a .spawn()'d callable with more than one positional arg has no established binding convention yet in calque's protocol; this is the same single-arg limitation checkInvokeSupport already flags for .starmap, not a NEW gap this function introduces
This is real: AI-Almanac's forecasts_app.py's actual .spawn()-invoked functions all take 3-4 positional args (run_forecast_inference(job_id, model_id, config), run_season_forecast_bundle(job_id, model_id, config, season_params)). A real calque spawn-run against that script (once calque#189's target-resolution fix lands, which it has) would build a real shard, ship the real body, but only bind job_id — model_id/config/season_params would be undefined inside the worker, the same class of failure calque#187 fixed for run.go's dry-run/real/fleet paths.
calque#187 does NOT cover this — its checkInvokeSupport arity guard is only called from run.go/realrun.go/fleetrun.go; spawnrun.go has its own separate driver (internal/exec/spawnshard.go) with no equivalent check at all.
Ask
Either:
- Extend the binding protocol so a spawned callable's
warmd/runner.py invocation can bind multiple positional args by name (mirroring .starmap()'s existing MethodArgs/Starmap splat mechanism in calexec.ManifestBody/worker/warm-runner/runner.py) when spawnArgsPayload produced a list — NamedShard/SpawnCallable would need to carry the full arg-name list, not just MethodArg.
- At minimum, add an arity guard to
spawnRunFromScript/BuildSpawnManifests refusing loudly (naming the callable and its real arg count) when a resolved SpawnCallable has 2+ non-self/cls args, instead of silently building a shard that will NameError on real hardware — the same honest-refusal pattern calque#187 established for the other three drivers.
Option 2 is the safe minimum; option 1 is the real fix and would make spawn-run actually usable against scripts like forecasts_app.py.
Context
Found while writing an end-to-end regression test for calque#189 (
TestSpawnDictDispatchEndToEndProducesRealShards,cmd/calque/spawn_dict_dispatch_e2e_test.go) — confirmingcalque spawn-run's pipeline actually produces a real shard, not just that call-site/classification resolve correctly.internal/exec/spawnshard.go'sSpawnCallable{MethodArg: f.ItemArg}(fromResolveSpawnCallables) only ever carries the callable's FIRST non-self/cls parameter name — same shapeir.Function.ItemArguses everywhere else in this codebase (the single-arg.map()-style binding protocol).spawnArgsPayload(same file) packs a call site's multiple real args into a list when there's more than one, but nothing on the callable-definition side has a binding convention for more than one name — the function's own doc comment already says this explicitly:This is real: AI-Almanac's
forecasts_app.py's actual.spawn()-invoked functions all take 3-4 positional args (run_forecast_inference(job_id, model_id, config),run_season_forecast_bundle(job_id, model_id, config, season_params)). A realcalque spawn-runagainst that script (once calque#189's target-resolution fix lands, which it has) would build a real shard, ship the real body, but only bindjob_id—model_id/config/season_paramswould be undefined inside the worker, the same class of failure calque#187 fixed forrun.go's dry-run/real/fleet paths.calque#187 does NOT cover this — its
checkInvokeSupportarity guard is only called fromrun.go/realrun.go/fleetrun.go;spawnrun.gohas its own separate driver (internal/exec/spawnshard.go) with no equivalent check at all.Ask
Either:
warmd/runner.pyinvocation can bind multiple positional args by name (mirroring.starmap()'s existingMethodArgs/Starmapsplat mechanism incalexec.ManifestBody/worker/warm-runner/runner.py) whenspawnArgsPayloadproduced a list —NamedShard/SpawnCallablewould need to carry the full arg-name list, not justMethodArg.spawnRunFromScript/BuildSpawnManifestsrefusing loudly (naming the callable and its real arg count) when a resolvedSpawnCallablehas 2+ non-self/cls args, instead of silently building a shard that will NameError on real hardware — the same honest-refusal pattern calque#187 established for the other three drivers.Option 2 is the safe minimum; option 1 is the real fix and would make
spawn-runactually usable against scripts likeforecasts_app.py.