cleanup for repoDriver
Opened by keeb · 4/24/2026· Shipped 4/24/2026
Problem
PR #1219 (swamp-club#159) shipped repo-level defaultDriver for workflow
execution, but the fix is incomplete and reviewer feedback surfaced additional
gaps. This issue tracks the cleanup work.
Scope (in)
1. Direct swamp model method run doesn't honor defaultDriver
The primary gap. src/libswamp/models/run.ts passes driver: input.driver
straight through; src/domain/models/method_execution_service.ts:604 resolves
via context.driver ?? "raw" and never calls resolveDriverConfig. Repo
default only applies to workflow execution today — direct method runs always
default to "raw" when no CLI flag is passed. This also means serve/HTTP
method-run paths (src/serve/connection.ts:266,
src/serve/open/http.ts:1780) don't pick up the repo default.
Fix: route the direct method-run path through resolveDriverConfig with
an effective chain of cli > definition > repo > "raw" (no step/job/workflow
tiers outside a workflow).
2. Proper memoization — fix the race, and extend it to the direct path
src/domain/workflows/execution_service.ts:1169-1175 memoizes the resolved
marker value, not the promise. Two concurrent run() calls both see
this.repoMarker === undefined, both issue a file read. Harmless (same data)
but breaks the "at most once" contract. Worse, the direct method-run path has
no memoization at all — every modelMethodRun invocation would re-read
.swamp.yaml unless addressed.
Fix: promise-memoize (this.markerPromise ??= this.markerRepo.read(...))
and share the same primitive via a loadRepoMarker dep in
ModelMethodRunDeps so both paths use it without duplicating infrastructure
instantiation inside libswamp.
3. Fix the definition tier consistently across paths
src/domain/workflows/execution_service.ts:523 has
ctx.driver ?? evaluatedDefinition.driver — dead code, since ctx.driver is
always defined after resolveDriverConfig returns "raw" as its fallback.
Model definitions with driver: set are silently ignored during workflow
execution. In the in-flight direct-method-run follow-up I opportunistically
wired evaluatedDefinition.driver into the direct path, which would make
direct and workflow paths diverge.
Fix: decide symmetrically — either wire the definition tier properly
into both paths via resolveDriverConfig, or drop it from both. Don't let
them diverge.
4. Audit pre-flight check path
Pre-flight checks build their own MethodContext somewhere. Verify they
don't carry stale driver semantics after the above changes.
5. Acknowledge and document the new failure mode
After this lands, malformed .swamp.yaml will fail every
swamp model method run with a parse error (previously direct runs didn't
read the marker). Consistent with workflow behavior, but worth calling out in
design/execution-drivers.md.
Scope (out — file as separate follow-ups)
- Named-fields object for
resolveDriverConfig(src/domain/drivers/driver_resolution.ts:44-51). Six positionalDriverSource | undefinedparams is fragile; call sites likeexecution_service.ts:1843-1853passundefinedfordefinitionjust to reachrepo. Move to{ cli?, step?, job?, workflow?, definition?, repo? }in a separate refactor issue. WorkflowExecutionService.execute()forwards fullrun()options (execution_service.ts:1389-1405). Currently dropsdriver(and others). Ergonomics fix; separate issue.- Runtime validation of
RepoMarkerDatafields.parseYaml(...) as RepoMarkerDatais an unchecked type assertion.defaultDriver: 123would survive parsing. Pre-existing pattern across all marker fields — needs a broader schema-validation pass rather than point fixes.
Acceptance criteria
swamp model method run <name> <method>in a repo withdefaultDriver: dockerin.swamp.yamlruns via the docker driver without passing--driver.--driver rawoverride still wins.- Serve/HTTP method-run paths behave the same way (inherit for free via
modelMethodRun). - Marker is read at most once per service instance / request lifecycle (promise-memoized), no concurrent double-reads.
- Direct path and workflow path handle the
definitiontier identically. - Pre-flight checks verified unaffected or updated symmetrically.
- Tests: unit + service-level for the direct path (mirroring the ones added for the workflow path in #1219); one concurrency test demonstrating the promise-memoize property; one integration test across both paths.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.