@@ -356,25 +356,28 @@ with ws.branch("strategy_a") as a:
356356 # a auto-commits into main on success
357357```
358358
359- ### Process isolation
359+ ### Process forking
360360
361- For untrusted or crash-prone agent code, ` BranchContext ` runs each task in
362- a sandboxed child process confined to its own branch via Landlock. No root
363- needed.
361+ For crash-prone agent code, ` BranchContext ` runs each task in a forked child
362+ process with its own process group. The child is automatically killed on
363+ timeout or context exit.
364+
365+ For sandboxing (filesystem confinement, resource limits, syscall filtering),
366+ combine with [ sandlock] ( https://github.com/multikernel/sandlock ) .
364367
365368``` python
366369from branching import BranchContext
367370
368- with ws.branch(" sandboxed " , on_success = None , on_error = None ) as fb:
369- with BranchContext(run_untrusted , workspace = fb.path) as ctx:
371+ with ws.branch(" forked " , on_success = None , on_error = None ) as fb:
372+ with BranchContext(run_agent , workspace = fb.path) as ctx:
370373 try :
371374 ctx.wait(timeout = 30 )
372375 fb.commit()
373376 except ProcessBranchError:
374377 fb.abort()
375378```
376379
377- Run N tasks in parallel, each in its own sandbox :
380+ Run N tasks in parallel, each in its own forked process :
378381
379382``` python
380383with BranchContext.create(
@@ -385,42 +388,6 @@ with BranchContext.create(
385388 ctx.wait(timeout = 60 )
386389```
387390
388- Any agent pattern can also opt into process isolation:
389-
390- ``` python
391- outcome = Speculate(candidates, isolate_processes = True , timeout = 60 )(ws)
392- ```
393-
394- ### Resource limits
395-
396- Constrain per-branch memory and CPU via setrlimit(2). Passing
397- ` resource_limits ` to any pattern automatically enables process isolation -
398- each branch runs in a forked child with limits enforced.
399-
400- ``` python
401- from branching import ResourceLimits, BestOfN
402-
403- limits = ResourceLimits(memory = 512 * 1024 * 1024 , cpu_time = 30 ) # 512 MB, 30s CPU
404-
405- outcome = BestOfN(candidates, resource_limits = limits)(ws)
406- ```
407-
408- All patterns accept ` resource_limits ` : ` Speculate ` , ` BestOfN ` , ` Reflexion ` ,
409- ` TreeOfThoughts ` , ` BeamSearch ` , ` Tournament ` , and ` Cascaded ` . Fields default to ` None `
410- (unlimited). A ` ResourceLimits() ` with all ` None ` fields triggers process
411- isolation without applying any limits.
412-
413- You can also pass limits directly to ` BranchContext ` :
414-
415- ``` python
416- from branching import BranchContext, ResourceLimits
417-
418- limits = ResourceLimits(memory = 1024 * 1024 * 1024 ) # 1 GB
419-
420- with BranchContext(run_agent, workspace = branch.path, limits = limits) as ctx:
421- ctx.wait(timeout = 30 )
422- ```
423-
424391## CLI
425392
426393The ` branching ` command exposes the agent patterns as shell commands.
@@ -435,8 +402,6 @@ Run a command in a new branch. Commits on exit 0, aborts on non-zero.
435402branching run -- ./build.sh
436403branching run --on-error none -- python train.py
437404branching run --ask -- make test # prompt before commit/abort
438- branching run --memory-limit 512M -- ./agent.sh # cap memory at 512 MB
439- branching run --memory-limit 1G --cpu-limit 0.5 -- python train.py
440405```
441406
442407### speculate
@@ -446,7 +411,6 @@ Race N commands in parallel branches. First success wins.
446411``` bash
447412branching speculate -c " ./fix_a.sh" -c " ./fix_b.sh" -c " ./fix_c.sh"
448413branching speculate --timeout 60 -c " python solve_v1.py" -c " python solve_v2.py"
449- branching speculate --memory-limit 256M -c " ./a.sh" -c " ./b.sh"
450414```
451415
452416### best-of-n
@@ -461,7 +425,6 @@ Each child receives `BRANCHING_ATTEMPT` (0-indexed) in its environment.
461425branching best-of-n -n 5 -- ./solve.py
462426branching best-of-n -n 3 --timeout 120 --json -- python attempt.py
463427branching best-of-n -n 3 -- bash -c ' python run.py && echo "$SCORE" >&3'
464- branching best-of-n -n 5 --memory-limit 1G --cpu-limit 0.5 -- python attempt.py
465428```
466429
467430### reflexion
@@ -475,7 +438,6 @@ The child receives `BRANCHING_ATTEMPT` (0-indexed) and `BRANCHING_FEEDBACK`
475438branching reflexion --retries 5 -- ./fix.sh
476439branching reflexion --retries 3 --critique " ./review.sh" -- ./solve.py
477440branching reflexion --retries 3 --critique " python critique.py" --json -- python agent.py
478- branching reflexion --retries 3 --memory-limit 512M -- ./fix.sh
479441```
480442
481443### status
@@ -496,20 +458,13 @@ first-winner-commit semantics.
496458
497459You just create a ` Workspace ` pointed at a mounted BranchFS path.
498460
499- Process isolation (` BranchContext ` ) uses fork + Landlock LSM + BPF LSM to
500- sandbox each child process. No namespaces, no cgroups, no root required:
501-
502- - ** Landlock LSM** (Linux 5.13+) confines each child to its own branch.
503- The child can read the filesystem outside the workspace but can only write
504- under its branch path. Sibling branches and the mount root are denied.
505- ` LANDLOCK_ACCESS_FS_REFER ` is included in the handled set so that
506- rename/link across the branch boundary is blocked.
507- - ** BPF LSM** provides inescapable process tracking. All descendants of a
508- branched process inherit the branch ID, enabling atomic teardown of an
509- entire branch's process tree. Requires ` CONFIG_BPF_LSM=y ` and
510- ` lsm=...,bpf,... ` in the kernel command line.
511- - ** setrlimit(2)** enforces per-branch resource limits (memory via
512- ` RLIMIT_AS ` , CPU time via ` RLIMIT_CPU ` , process count via ` RLIMIT_NPROC ` ).
513- Lightweight alternative to cgroups -- no cgroupfs infrastructure needed,
514- limits are inherited by children on fork.
461+ Process forking (` BranchContext ` ) uses ` fork(2) ` + process groups to run
462+ each task in an isolated child process. The child's working directory is set
463+ to the branch path, and ` mprotect(2) ` enforces copy-on-write invariants on
464+ parent memory regions.
465+
466+ BranchContext focuses purely on branching. For sandboxing (filesystem
467+ confinement, syscall filtering, resource limits), use
468+ [ sandlock] ( https://github.com/multikernel/sandlock ) alongside branching --
469+ the two are designed to compose together.
515470
0 commit comments