Skip to content

feat(multi-tenancy): per-session workspace_root into praxis tool execution (BRO-1491)#1771

Open
broomva wants to merge 2 commits into
mainfrom
feature/bro-1491-thread-per-session-manifestworkspace_root-into-praxis-tool
Open

feat(multi-tenancy): per-session workspace_root into praxis tool execution (BRO-1491)#1771
broomva wants to merge 2 commits into
mainfrom
feature/bro-1491-thread-per-session-manifestworkspace_root-into-praxis-tool

Conversation

@broomva

@broomva broomva commented Jul 9, 2026

Copy link
Copy Markdown
Owner

What

Threads per-session manifest.workspace_root through the arcan→praxis tool-execution path so concurrent sessions get isolated workspaces (multi-tenancy isolation). Implements the BRO-1490 follow-up per the ticket's code-verified design notes: ToolContext field + bridge.rs copy-through, praxis FsPort scoping, session-unique lago-fs manifest keys, and BashTool cwd following the session workspace.

27 files, +1093/−120 across arcan-{aios-adapters,core,harness,lago}, praxis-{core,tools}, lago-fs, plus praxis_integration.rs + end_to_end.rs tests.

⚠️ Provenance & validation

Autonomous arc-authored — the first live dispatch of the VPS Life-governor (BRO-1744). The arc completed the code but died before its own test run (the arc-stall failure mode being fixed in BRO-1831). So this is arc-authored + arc-unvalidated: CI is the validator, and this needs human/P20 review before merge. Do not merge until CI green + reviewed.

Acceptance (from the ticket)

Two concurrent sessions write artifacts/receipt.txt; each lands in its own {data_dir}/sessions/<id>/artifacts/, two distinct FileWrite events with session-unique manifest paths, neither session reads the other's file.

Linear: BRO-1491 · recovered via BRO-1744 · stall-fix BRO-1831

Summary by CodeRabbit

  • New Features

    • Tools and shell commands now honor a per-session workspace root, helping keep file operations isolated to the current session.
    • Session-scoped filesystem tracking now preserves changes and events separately for each workspace.
  • Bug Fixes

    • Prevents file operations from escaping the active session workspace.
    • Improves consistency so reads, writes, searches, and edits follow the same session boundary rules.
    • Updates serialized context handling so the new workspace setting is included only when present.

…ution (BRO-1491)

Autonomous arc implementation — VPS Life-governor first dispatch (BRO-1744). Threads per-session manifest.workspace_root through ToolContext -> bridge -> praxis FsPort scoping + session-unique manifest keys + BashTool cwd, per the ticket design notes. 27 files, BRO-1490 follow-up.

ARC-AUTHORED + UNVALIDATED: the arc died before its own test run completed. CI is the validator — do not merge until green + reviewed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR threads a per-session workspace_root through ToolContext (kernel protocol and Arcan runtime), propagates it across bridges and adapters, adds a scoped capability to FsPort/LocalFs, updates filesystem/shell/edit tools to use session-scoped filesystem handles, and extends Lago's manifest, tracker, and tracked-fs with session-unique key reconciliation. ReconcilingTool and application wiring in main.rs are updated to apply session-scoped reconciliation, with integration tests validating per-session isolation.

Changes

Per-session workspace root scoping

Layer / File(s) Summary
ToolContext contract additions
crates/aios/aios-protocol/src/tool.rs, crates/arcan/arcan-core/src/runtime.rs
Adds optional workspace_root: Option<String> field to ToolContext in both kernel protocol (serde skip-if-none) and Arcan runtime (with Default derive), plus roundtrip/serialization tests.
Bridge propagation
crates/arcan/arcan-harness/src/bridge.rs
Carries workspace_root from Arcan ToolContext into the proto ToolContext across the bridge, with a new preservation test.
Adapter wiring and test defaults
crates/arcan/arcan-aios-adapters/src/tools.rs, .../gating_middleware.rs, .../haima_middleware.rs, crates/arcan/arcan-lago/src/*.rs, crates/arcan/arcan-opsis/src/tools.rs, crates/arcan/arcan-praxis/src/registry.rs, crates/arcan/arcan-spaces/src/*.rs, crates/arcan/arcan/src/shell.rs, crates/nous/nous-middleware/src/middleware.rs
Maps request.workspace_root into ToolContext in the AIOS adapter execute path, and updates test helper ToolContext literals throughout to use ..Default::default().
FsPort scoped trait and LocalFs implementation
crates/praxis/praxis-core/src/fs_port.rs, crates/praxis/praxis-core/src/local_fs.rs
Adds a scoped method to FsPort (default None) and implements it in LocalFs by rebasing FsPolicy to a session root, with isolation tests.
Praxis tools use effective_fs
crates/praxis/praxis-tools/src/fs.rs, .../edit.rs, .../shell.rs
Adds effective_fs helper and updates read/write/list_dir/glob/grep/edit/shell tools to derive session-scoped filesystem/sandbox handles from ToolContext.workspace_root.
Lago manifest/tracker/tracked-fs session keys
crates/lago/lago-fs/src/manifest.rs, .../tracker.rs, crates/arcan/arcan-lago/src/tracked_fs.rs
Adds Manifest::subtree/replace_subtree, FsTracker::reconcile_bounded_scoped, and session-unique manifest key rebasing plus scoped FsPort support in LagoTrackedFs.
ReconcilingTool session-aware reconciliation
crates/arcan/arcan-lago/src/reconciling_tool.rs
Adds session_base, SessionScope, and context-aware reconcile_and_emit to select scoped or unscoped reconciliation per call.
Application wiring and integration tests
crates/arcan/arcan/src/main.rs, crates/arcan/arcan/tests/praxis_integration.rs
Configures session_base for LagoTrackedFs and ReconcilingTool in main.rs, and updates full-stack tests to assert per-session workspace isolation, including a two-session acceptance test.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Adapter as ArcanHarnessAdapter
  participant Bridge as ArcanHarnessBridge
  participant Tool as WriteFileTool/BashTool
  participant FsPort as effective_fs/LocalFs.scoped
  participant Lago as LagoTrackedFs/FsTracker

  Adapter->>Adapter: build ToolContext with workspace_root from request
  Adapter->>Bridge: arcan_ctx_to_proto(ctx)
  Bridge->>Tool: execute(ctx with workspace_root)
  Tool->>FsPort: effective_fs(base, ctx)
  FsPort->>FsPort: scoped(workspace_root)
  Tool->>Lago: write/read via scoped FsPort
  Lago->>Lago: manifest_key(path) rebased to session prefix
  Lago->>Lago: reconcile_bounded_scoped(walk_root, key_prefix)
  Lago-->>Tool: FileWrite/FileDelete events scoped to session
Loading

Possibly related issues

  • broomva/life#1743: Directly related — implements the same per-session workspace_root threading through ToolContext, bridges/adapters, filesystem tools, and shell/reconciliation for session isolation.

Possibly related PRs

  • broomva/life#1726: Introduced ReconcilingTool and its workspace-rooted post-exec reconciliation, which this PR extends with session-scoped reconciliation.
  • broomva/life#1742: Established workspace-root setup in main.rs for file tools, which this PR builds on with with_session_base/ToolContext.workspace_root.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: propagating per-session workspace_root through Praxis tool execution for multi-tenancy.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/bro-1491-thread-per-session-manifestworkspace_root-into-praxis-tool

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
crates/arcan/arcan/tests/praxis_integration.rs (1)

447-452: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Comment overstates what the assertion checks. The assert_ne! just re-compares the two on-disk contents (already asserted equal to their expected values above); it does not exercise a session-A read that traverses into session B, so it doesn't demonstrate boundary rejection. Consider adjusting the comment (or dropping the redundant assert) to avoid implying isolation coverage that lives in the tracked_fs unit tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/arcan/arcan/tests/praxis_integration.rs` around lines 447 - 452, The
current comment in the praxis integration test overstates what the trailing
assertion verifies, since the assert_ne! only compares the two files’ on-disk
contents and does not test a session-A read crossing into session B. Update the
surrounding comment near the a_file/b_file assertions to describe only what is
actually checked, or remove the redundant assert_ne! if it adds no value; keep
the real boundary-isolation coverage in the tracked_fs tests and avoid implying
this test exercises it.
crates/lago/lago-fs/src/tracker.rs (1)

240-248: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Scoped reconcile disables hash reuse — re-hashes the whole session tree on every command.

Passing Manifest::new() as the previous view means snapshot_bounded's size+mtime fast path never fires, so every file in the session workspace is re-read and re-put into the blob store on each exec-path reconcile. reconcile_bounded avoids this via the manifest reuse hint. With RemoteBlobBackend (LAGO_URL set) this becomes a network put per file per shell command. You can rebuild the reuse hint by stripping key_prefix off the current subtree:

⚡ Restore hash reuse for the scoped walk
// Previous view keyed relative to walk_root (`/rel`) so snapshot_bounded
// can reuse unchanged hashes instead of re-reading + re-putting every file.
let prev = {
    let manifest = self.manifest.lock().unwrap();
    let mut m = std::collections::BTreeMap::new();
    for (k, e) in manifest.subtree(key_prefix) {
        if let Some(rel) = k.strip_prefix(key_prefix) {
            let mut e = e.clone();
            e.path = rel.to_string();
            m.insert(rel.to_string(), e);
        }
    }
    Manifest::from_entries(m)
};
let raw = snapshot::snapshot_bounded(walk_root, &prev, self.blob_store.as_ref(), limits)?;

Confirm whether the scoped exec path runs against remote lagod in production, which would amplify the per-command cost.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lago/lago-fs/src/tracker.rs` around lines 240 - 248, Restore hash
reuse in the scoped exec reconcile path by replacing the unconditional
Manifest::new() passed to snapshot::snapshot_bounded in
Tracker::reconcile_bounded with a previous-view manifest built from the current
subtree under key_prefix. Use the existing manifest lock and subtree lookup to
strip key_prefix from keys, clone each entry, and rebase entry.path to the
walk_root-relative path so snapshot_bounded can hit its size+mtime fast path
instead of re-hashing and re-putting every file. Keep the change localized to
the session workspace snapshot logic in tracker.rs and preserve the existing
snapshot_bounded call shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/arcan/arcan/tests/praxis_integration.rs`:
- Around line 447-452: The current comment in the praxis integration test
overstates what the trailing assertion verifies, since the assert_ne! only
compares the two files’ on-disk contents and does not test a session-A read
crossing into session B. Update the surrounding comment near the a_file/b_file
assertions to describe only what is actually checked, or remove the redundant
assert_ne! if it adds no value; keep the real boundary-isolation coverage in the
tracked_fs tests and avoid implying this test exercises it.

In `@crates/lago/lago-fs/src/tracker.rs`:
- Around line 240-248: Restore hash reuse in the scoped exec reconcile path by
replacing the unconditional Manifest::new() passed to snapshot::snapshot_bounded
in Tracker::reconcile_bounded with a previous-view manifest built from the
current subtree under key_prefix. Use the existing manifest lock and subtree
lookup to strip key_prefix from keys, clone each entry, and rebase entry.path to
the walk_root-relative path so snapshot_bounded can hit its size+mtime fast path
instead of re-hashing and re-putting every file. Keep the change localized to
the session workspace snapshot logic in tracker.rs and preserve the existing
snapshot_bounded call shape.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bd3ecf3f-66ea-4775-8009-7c32e33b4832

📥 Commits

Reviewing files that changed from the base of the PR and between 860a473 and 8aee916.

📒 Files selected for processing (27)
  • crates/aios/aios-protocol/src/tool.rs
  • crates/arcan/arcan-aios-adapters/src/gating_middleware.rs
  • crates/arcan/arcan-aios-adapters/src/haima_middleware.rs
  • crates/arcan/arcan-aios-adapters/src/tools.rs
  • crates/arcan/arcan-core/src/runtime.rs
  • crates/arcan/arcan-harness/src/bridge.rs
  • crates/arcan/arcan-lago/src/learning.rs
  • crates/arcan/arcan-lago/src/memory_tools.rs
  • crates/arcan/arcan-lago/src/policy_middleware.rs
  • crates/arcan/arcan-lago/src/reconciling_tool.rs
  • crates/arcan/arcan-lago/src/tracked_fs.rs
  • crates/arcan/arcan-lago/tests/end_to_end.rs
  • crates/arcan/arcan-opsis/src/tools.rs
  • crates/arcan/arcan-praxis/src/registry.rs
  • crates/arcan/arcan-spaces/src/middleware.rs
  • crates/arcan/arcan-spaces/src/tools.rs
  • crates/arcan/arcan/src/main.rs
  • crates/arcan/arcan/src/shell.rs
  • crates/arcan/arcan/tests/praxis_integration.rs
  • crates/lago/lago-fs/src/manifest.rs
  • crates/lago/lago-fs/src/tracker.rs
  • crates/nous/nous-middleware/src/middleware.rs
  • crates/praxis/praxis-core/src/fs_port.rs
  • crates/praxis/praxis-core/src/local_fs.rs
  • crates/praxis/praxis-tools/src/edit.rs
  • crates/praxis/praxis-tools/src/fs.rs
  • crates/praxis/praxis-tools/src/shell.rs

…ad-per-session-manifestworkspace_root-into-praxis-tool
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.

1 participant