Skip to content

fix(runtime): withhold the history view and root anchor from runtimes - #932

Merged
zechengz merged 1 commit into
strukto-ai:mainfrom
bytecii:refactor/runtime-doors
Aug 27, 2026
Merged

zechengz merged 1 commit into
strukto-ai:mainfrom
bytecii:refactor/runtime-doors

Conversation

@bytecii

@bytecii bytecii commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

What

Python announced the unfiltered mount table to sandboxed runtimes; TypeScript filters it (sandboxVisibleMounts). This PR ports the filter and pins it in both languages, and writes the runtime tier's session-blindness doctrine down on LanguageRuntime.

The divergence

The workspace hands every LanguageRuntime a resolver at attach time — the mount prefixes a guest may see (WASI preopens one directory per mount; RuntimeVFS.prefixes()/mount_of route on them; PolicyContext.mounts is stamped from the same resolver).

  • TypeScript (workspace.ts sandboxVisibleMounts) withholds two prefixes, neither for being /: the history view (a shell surface, not a place to put files) and the synthetic root anchor (a mount nobody asked for — the workspace adds it so arg-less commands resolve). An explicit root mount is forwarded like any other prefix.
  • Python passed self._ops.mount_prefixes unfiltered, so a WASI guest preopened /.bash_history and every runtime reported a claim on the anchor. PrefixResolver's own docstring already promised "a sandbox-filtered list for the runtimes" — the docstring described TS, not Python.

Changes

  • workspace.py: new Workspace._sandbox_visible_mounts() (live read off the registry, mirroring TS), wired into the one PrefixResolver construction. PolicyContext.mounts follows the same resolver in both languages, so policies now see the same list too.
  • tests/workspace/test_runtime_binding.py: a ResolverProbe runtime captures the attached resolver; three pins — history withheld, synthetic anchor withheld, explicit root forwarded (guards against over-filtering). The two "withholds" pins were verified red against the old wiring.
  • workspace.test.ts: the same three pins. TS behavior was already correct but had no direct test.
  • language.py / language.ts: doctrine paragraph — the doors are the data plane (dispatch) and the name plane (resolver), and the list is complete on purpose: there is no session door. A guest env write lands on the guest's own copy and dies with the run, so guest code can neither trip nor bypass a pre_session rule. A runtime that ever needs session state must take a gated SessionView.

What this PR deliberately does not do

This came out of a design review of how state planes reach the runtime tier (D4: "runtimes have no doors record"). The survey falsified the record: attach(dispatch, resolver) already is the two doors — data plane and name plane — mirrored field-for-field across languages, with MountResolver documented as "the name-plane questions a runtime asks" and growth landing as protocol methods fed by injected sources, not signature changes. A RuntimeDoors record would additionally have Python-only payload (the session/recorder re-bind exists only where guest calls cross threads; TS has no ambient capture at all), violating the parity it was meant to serve. What the survey did surface is this filter divergence, which is the PR.

🤖 Generated with Claude Code

The workspace announces its mount table to sandboxed runtimes through
the resolver attached at construction, and TypeScript filters that
list (sandboxVisibleMounts): the history view is a shell surface
rather than a place to put files, and the synthetic root anchor is a
mount nobody asked for. Python handed the unfiltered Ops list, so a
WASI guest preopened /.bash_history and every runtime reported a
claim on the anchor -- while PrefixResolver's own docstring already
promised the filtered behavior. PolicyContext.mounts follows the same
resolver in both languages, so a policy now sees the same list too.

Both withholds are pinned by a probe runtime in each language (the
python pins verified red against the old wiring), plus a guard that
an explicit root mount is still forwarded: withheld for being
synthetic, never for being /.

Also writes the session-blindness doctrine down on LanguageRuntime in
both languages: the doors are the data plane and the name plane, and
there is deliberately no session door -- a guest env write dies with
the run and can neither trip nor bypass a pre_session rule.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bytecii
bytecii requested a review from zechengz as a code owner August 27, 2026 19:02

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@zechengz

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: 1c5bf0b474

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@zechengz
zechengz merged commit 0645461 into strukto-ai:main Aug 27, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants