-
Notifications
You must be signed in to change notification settings - Fork 2
plat 284
PLAT-284 — Packages a step installed were thrown away with the run folder, so every run reinstalled them from PyPI
| Coordination | Value |
|---|---|
| Assigned agent | Claude Fable 5.1 |
| Ticket state |
fixed, live-verified on the Dominion deployment |
| Last synchronized | 2026-09-05 |
-
Priority: P2 — PLAT-283 made installs possible; this makes them
sensible. Without it every run pays a full PyPI download (~15 packages
for
yfinance) and inherits PyPI/network/version-drift risk on every single run, forever — the trading agent's own follow-up objection, and a correct one. -
Owner:
workspace/security/sandbox_tool_env.go(routing, all three backends),agent_go/pkg/orchestrator/agents/workflow/step_based_workflow/controller_agent_factory.go(setupExecutionFolderGuardgrant),agent_go/pkg/workspace/advanced_tools.go(tool description), guidancecode-authoring.md/backup-strategy.md,agent_go/cmd/server/workflow_backup.go(hash skip). -
Related: PLAT-283 (installs at all), PLAT-281 (deployment-installed
CLIs in
/srv/dominion/tools— the box-level complement to this).
PLAT-283's routing chose the step's WorkDir as the base for pip's user
site and cache. Live on Dominion that is
runs/iteration-0/default/execution — the per-run folder that rotates on
the next run — so an install survived exactly one run. The agent's framing
("a completely fresh, disposable sandbox with nothing carried over") was
half wrong, though: the policy is per-run, the filesystem is not. The
same live [SHELL ISOLATOR] line shows the workflow root
Workflow/tectonicusadaytrading in the step's write grant, and it persists.
There was already a place that survives runs; nothing pointed pip at it.
Two related facts that shaped the fix: a step can never install
system-wide (/usr is read-only to it and the service user has no sudo —
that is the sandbox doing its job), so "persistent" can only mean
per-workflow; and the default step grant is only its own step folder +
Downloads + db — the trading workflow's root-wide write is the exception,
so the persistent folder needs an explicit grant to work everywhere.
- One reserved folder per workflow,
Workflow/<id>/.sandbox-cache/(security.SandboxPersistentDirName).setupExecutionFolderGuardgrants it writable to every execution step (and generic agents), so it is materialized like every other platform-managed grant on first use. - The sandbox recognises the folder by name in the write grant — the two
modules only meet over HTTP, so the name is the contract, no new request
plumbing — and routes into it:
PYTHONUSERBASE,PIP_CACHE_DIR,XDG_CACHE_HOME,npm_config_cache+npm_config_prefix,GOPATH/GOCACHE,CARGO_HOME,PIPX_HOME/PIPX_BIN_DIR; itsbin/,python/bin,npm-global/bin,go/bin,cargo/binleadPATH; andSANDBOX_PERSISTENT_DIRnames it so an agent can put a venv or a downloaded binary there deliberately.TMPDIRstays in the run folder — scratch must not accumulate in the one place that survives. Without the grant (older orchestrator, non-workflow caller) PLAT-283's run-folder fallback stands unchanged. - The routing moved out of the Landlock-only file into a shared
sandbox_tool_env.goand is applied by all three backends (Landlock, mount namespace, macOS Seatbelt), so a workflow installs the same way on a dev Mac as on a Linux box — the macOS default profile never blocked$HOME, which is why none of PLAT-281/283/284 ever reproduced locally. - Agents are told: the
execute_shell_commanddescription and the code-authoring guidance now say installs work and persist, distinguish$SANDBOX_PERSISTENT_DIR(tooling) fromdb/assets/(files), and say root/aptis not possible from a step. - Backups skip it (
shouldSkipBackupHashFile, alongsideruns/), and the backup-strategy guidance lists it with the other never-back-this-up folders.
- Unit:
sandbox_tool_env_test.go(persistent routing, run-folder fallback, unwritable WorkDir, no-grant no-op, persistent-only TMPDIR, theIsolator.toolEnvpath the macOS/mount backends use) run on macOS and, cross-compiled, on Dominion; the folder-guard suite with a new subtest pinning the grant;TestBackupHashSkipsTheSandboxCache. - Live on Dominion after deploy: see the register row / the end of this
ticket for the two-run proof — run 1
pip install --user yfinancedownloads and installs into.sandbox-cache/python; run 2, in a fresh run folder,pip install --user --no-index yfinancesucceeds —--no-indexforbids any network, so it can only pass if the install truly persisted.
Auto-synced from docs/ on main. Edit there, not here.