softwaremill / softwaremill/ox
Adopt capture checking: capture-aware fork/Flow signatures, tracked Ox capability
Nobody has claimed this yet.
- Dominant language
- Scala
- Stars
- 522
- Forks
- 35
- Avg merge
- 45m
- Merged PRs (30d)
- 13
Description
Motivation
In orca we split side-effect gating into capability tokens — a shared one that may legally cross fork boundaries (LLM-call gate, caps.SharedCapability) and exclusive ones that must not (workspace/git-index writers, caps.ExclusiveCapability) — and we want Scala's capture + separation checking to reject an exclusive capability captured into a fork closure at compile time.
That works today, but only through a wrapper, because of an empirically verified limitation: separation checking does not fire through combinators of a library that was not itself compiled under capture checking. Calling ox.fork or ox.flow.Flow.mapParUnordered from a CC-enabled file yields zero enforcement — the checker needs the combinator's own signature to declare what the closures may capture.
Our bridge is CheckedPar — a 1:1 delegate to Flow.mapParUnordered, compiled under captureChecking + separationChecking, whose capture-set parameter (thunks: Seq[() ->{C} T] with C^) makes the call site checkable. With it, a closure capturing an exclusive capability is a hard "Separation failure" compile error while shared capabilities pass — both directions pinned by a dotc-in-test negative-compile suite. The wrapper is explicitly a bridge designed for deletion once Ox carries the annotations natively (design record: ADR 0018 §6).
What Ox would need
- Capture-aware signatures on the closure-accepting concurrency API —
fork/forkUser/etc.,supervised/unsupervisedbodies, and theFlowparallel combinators (mapPar,mapParUnordered, …), so separation checking evaluates each caller's closure captures at the call site. In our experiments, plain=>-style params on a CC-compiled combinator suffice for the hidden-set rules to engage; an explicit capture-set type parameter (as inCheckedPar) gives callers something to bind. - Track the
Oxcapability itself — so a fork handle orOx-scoped resource escaping itssupervisedscope becomes a compile error. Downstream this would also let us make "agent value outlives its owning scope" a compile error rather than a runtime latch.
Findings from our adoption that may save you time (all verified on stock Scala 3.8.4, no nightly, no flags)
caps.Capabilityis sealed;caps.SharedCapabilityis non-experimental (extendable with zero language imports),caps.ExclusiveCapabilityis@experimental(defining files need the CC import).- No consumer taint for definitions: a jar built from CC-importing files is consumable by plain non-CC consumers with zero flags. But a signature carrying a capture-set parameter (
C^) is only callable from CC-enabled compilation units — an API-surface consequence worth designing around (it's whyCheckedParis deliberately not on orca's exported surface). - Enforcement locus is the caller's compilation unit: both the combinator and each calling file need the language imports for violations to be caught.
- Tooling caveats: scalafmt (3.8.3) cannot tokenize the
^syntax (per-file exclusion needed); tapir'sderivesschema derivation doesn't typecheck under CC (keep macro-deriving types out of CC-compiled files); munit'scompileErrors/typeCheckErrorsare blind to CC violations (CC runs post-typer) — negative tests need separate compilation.
Happy to share more details from the adoption or test fixtures if useful.
🤖 Generated with Claude Code
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the CheckedPar.scala bridge, CcNegativeCompileTest.scala, and ADR 0018 §6, then inspect Ox's fork, supervised, and Flow parallel-combinator APIs. Define capture-aware signatures and tracking for the Ox capability, with separate compilation tests showing exclusive captures and escaping scoped resources fail while shared capabilities pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- scala
- Domain
- backend, backend-api-design
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100