Skip to content

feat: auto-materialise static workspace when path is empty or missing #516

Description

@christso

Objective

When workspace.mode=static with a workspace.path, the orchestrator should auto-materialise the workspace (template + repos) at the defined path if the directory doesn't exist or is empty — reusing it as-is when it already has content.

Currently, static mode sets sharedWorkspacePath = configuredStaticPath unconditionally and skips all materialisation. This means a fresh static path with no pre-existing content gets no template copy, no repo cloning, and no setup hooks — the agent runs against an empty directory.

Current behaviour

orchestrator.ts ~L519:
if (useStaticWorkspace && configuredStaticPath) {
    sharedWorkspacePath = configuredStaticPath;
    setupLog(`using static workspace: ${configuredStaticPath}`);
}

No existence/emptiness check. No template or repo materialisation.

Desired behaviour

Follow a workflow similar to pooled mode:

  1. Path exists and has content → reuse as-is (current behaviour, no change)
  2. Path does not existmkdir -p, then materialise (copy template, clone repos, run setup hooks) at that path
  3. Path exists but is empty → materialise in place (copy template, clone repos, run setup hooks)

This mirrors how WorkspacePoolManager.acquireWorkspace conditionally creates/reuses pool slots, but applied to a user-specified static path.

Hooks in static mode

Setup hooks (workspace.hooks.before_all, before_each, etc.) run by default on static workspaces — both on initial materialisation and on reuse. This ensures consistent workspace state across runs.

A workspace.hooks.enabled option (default: true) allows disabling hooks for static workspaces when the user manages workspace state externally or wants to skip redundant setup on reuse.

workspace:
  mode: static
  path: /my/workspace
  hooks:
    enabled: false        # skip all workspace hooks
    before_all:
      command: [setup.sh]

When hooks.enabled: false, no workspace hooks fire regardless of whether the workspace is freshly materialised or reused.

Design latitude

  • Reuse the existing createTempWorkspace / template-copy logic but target the static path instead of a generated temp dir
  • Repo cloning via RepoManager should work identically to pooled mode
  • An empty directory can be detected by checking for zero entries (excluding . and ..)
  • Should log clearly whether the static workspace was reused or materialised
  • hooks.enabled only needs to apply to static mode initially; pooled/temp always run hooks

Acceptance signals

  • workspace.mode=static + workspace.path=/some/new/path + workspace.template → template is copied to /some/new/path, repos cloned, setup hooks run
  • workspace.mode=static + workspace.path=/some/existing/populated/path → reused, hooks still run by default
  • workspace.mode=static + workspace.path=/some/empty/dir → materialised in place
  • workspace.hooks.enabled: false → no hooks fire on static workspace (materialised or reused)
  • workspace.hooks.enabled omitted or true → hooks fire normally (default)
  • Tests cover all three materialisation cases + hooks enabled/disabled
  • Existing static workspace tests continue to pass

Non-goals

Related

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