Skip to content

Windows agents never receive the [Workspace] system-prompt section: workspace_section() gates on a POSIX-only starts_with('/') predicate #3124

Description

@kcao-gss

Describe the bug

workspace_section() gates the [Workspace] system-prompt block on a raw byte-prefix test for /, so it is silently omitted for every Windows agent. crates/buzz-acp/src/pool.rs:1165-1177:

fn workspace_section(cwd: &str) -> Option<String> {
    if cwd != "/" && cwd.starts_with('/') {
        Some(format!(
            "[Workspace]\nYour absolute working directory is `{cwd}`. All workspace \
             files — `AGENTS.md`, `RESEARCH/`, `PLANS/`, `GUIDES/`, `WORK_LOGS/`, \
             `OUTBOX/` — and any repositories you clone (under `{cwd}/REPOS/`) live \
             here. This is where you already are; do not search `$HOME` or other \
             directories for them."
        ))
    } else {
        None
    }
}

cwd originates from std::env::current_dir() (lib.rs:1547-1550), which on Windows always returns drive-letter form (C:\Users\<user>\.buzz). That fails starts_with('/'), so the block is dropped. There is no cfg(windows) branch and no compensating path (rg 'cfg\(windows\)' over crates/buzz-acp/src yields one unrelated hit, acp.rs:1998).

Scoping this honestly, because the obvious reading overstates it: this is not "Windows agents don't know about REPOS//OUTBOX/." That information is delivered unconditionally on every platform:

  • base_prompt.md:84-98 — the full Workspace Layout table, OUTBOX/ and REPOS/ included.
  • base_prompt.md:100 — the anti-$HOME-scan instruction, i.e. the omitted section's own stated purpose, is also already unconditional.
  • The agent's literal OS cwd is the workspace: the child inherits it (no current_dir call anywhere in acp.rs) from the harness spawn at runtime.rs:566-569, so ls works.
  • AGENTS.md on disk repeats the layout (nest.rs:190-194, nest_agents.md:13-14).

So what Windows actually loses is one reinforcing statement of the absolute workspace root inside the system prompt. Expected symptom is a probabilistic uptick in path-guessing or wasted exploration on Windows, not a functional failure. Filing it as a correctness bug rather than a user-facing breakage: it's a one-line predicate fix with a trivially-added test, and a starts_with('/') path predicate left in the tree invites the same mistake again.

Steps to reproduce

The predicate is pure string logic, so this needs no Windows host — the following test fails today on Linux/macOS CI:

// crates/buzz-acp/src/pool.rs, mod tests
#[test]
fn test_workspace_section_windows_cwd_is_some() {
    assert!(workspace_section(r"C:\Users\me\.buzz").is_some());
}

Verified by executing the identical predicate standalone:

         /Users/me/.buzz -> true
       C:\Users\me\.buzz -> false   <-- Windows cwd
       C:/Users/me/.buzz -> false
                       / -> false   (intended sentinel rejection)

End-to-end on Windows: spawn a managed agent and inspect the session/new systemPrompt (the desktop transcript observer renders the section — see agentSessionTranscriptHelpers.test.mjs:327-349). It begins at [Base] with no preceding [Workspace] block; the same setup on macOS begins [Workspace]\nYour absolute working directory is /Users/.../.buzz....

Expected behavior

Windows agents receive the [Workspace] section with their real absolute workspace path, as macOS and Linux agents do.

Version and platform

  • Buzz version: main @ 87b3fcd3. Gate is unmodified since introduction, so every release since 2026-06-23 is affected.
  • OS: Windows (all versions). macOS and Linux unaffected — their cwd passes the gate.
  • Found by code inspection, not a field report. No user has reported the symptom.

Logs / additional context

Origin. Introduced in 1011cea2682a5e8cd92c9da255d5ba6c8f7ced78, "fix(desktop): ground agent workspace, migrate legacy nest, configurable repos_dir" (#1194), 2026-06-23. Never modified since. The guard's own doc comment explains its intent — filtering relative paths and the / fallback that current_dir() produces on failure, because a /-rooted workspace line "would actively encourage the $HOME-wide scan this section exists to prevent." The motivating case in that diff was macOS TCC prompts. Windows appears simply not to have been in scope: every test added alongside it uses POSIX paths (/, /Users/me/.buzz, relative/path, ""). (That last point is inference from the absence of any Windows path in the diff, comment, or tests — nothing expresses an intent to exclude Windows.)

Existing tests — five cover this gate, none with a Windows-style cwd: pool.rs:3809-3822, :3824-3831, :3833-3838, :3840-3844, plus the framed_system_prompt set at :3783-3807.

Suggested fix (one line + one test): replace the prefix test with std::path::Path::new(cwd).is_absolute(), keeping a root/sentinel rejection. Path::parent().is_some() rejects /, C:\, and \\?\C:\ uniformly; retaining the literal cwd != "/" also works, since lib.rs:1548's fallback hardcodes it.

is_absolute's platform-dependence is correct here rather than a hazard: the string comes from current_dir() in the same process on the same host, so the compile-time platform always matches the path's origin. No caller supplies a cwd from elsewhere. Two caveats for whoever takes it:

  1. On Windows is_absolute() is has_root() && prefix().is_some(), so drive-relative (\foo) and bare-drive (C:) forms stay false. Neither is producible by current_dir(), so it doesn't matter in practice — just don't describe the fix as "any Windows path works."
  2. Do not fix this by normalizing the cwd string at lib.rs:1547. That same string is the ACP session/new cwd (pool.rs:831); changing it alters protocol behavior for every harness.

Two follow-on notes, both out of scope for the one-line fix:

  • Once Windows agents start receiving the section, its $HOME phrasing (and base_prompt.md:100's) reads wrong on Windows — %USERPROFILE% may be worth adding.
  • {cwd}/REPOS/ at pool.rs:1170 will render C:\Users\me\.buzz/REPOS/ — mixed separators, functional but ugly.

Separate, larger gap found while confirming this (platform-independent, not filed): [Workspace] rides only the session/new systemPrompt. session_new_system_prompt returns None for protocol_version < 2 (pool.rs:185-195), and the legacy user-message framing path (prepend_base_for_legacy, pool.rs:1090-1098, :1593; queue.rs:1431) emits [Base] but never [Workspace]. So protocol-v1 harnesses lack the anchor on macOS and Linux too. Happy to split that out if maintainers want it tracked.

Duplicate search: searched issues for "workspace prompt", "OUTBOX", and PRs for "base_prompt workspace" — none found. Nearest is #2334 (base prompt shown as session title in harness UIs), which is a display concern, not this gate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions