# How SecureDesign Handles Multi-Root Workspaces in VS Code: Implementation Deep Dive

> Learn how SecureDesign manages VS Code multi-root workspaces by isolating state and data with deterministic IDs and dedicated file watchers for each root. Optimize your workflow.

- Repository: [Harold Martin/secure-design](https://github.com/hbmartin/secure-design)
- Tags: 
- Published: 2026-03-03

---

**SecureDesign isolates state, file watching, and design storage across VS Code multi-root workspaces by generating deterministic workspace IDs from sorted folder URIs, namespacing all persisted data, and creating dedicated file watchers for each root folder.**

SecureDesign is a VS Code extension that manages secure design files and chat history across varying project structures. The extension is engineered to function identically whether you open a single folder or a complex multi-root workspace containing multiple unrelated projects. According to the `hbmartin/secure-design` source code, the extension achieves this through five key architectural mechanisms that ensure complete data isolation per workspace root.

## Generating Stable Workspace Identifiers

The foundation of multi-root support begins with deterministic workspace identification. In [`src/services/workspaceStateService.ts`](https://github.com/hbmartin/secure-design/blob/main/src/services/workspaceStateService.ts), the `getWorkspaceId()` method (lines 41-58) creates a unique, order-independent identifier that remains stable regardless of how folders are arranged in the workspace.

For a **single-folder workspace**, the method returns the folder URI directly. For **multi-root workspaces**, the implementation:

- Collects URIs from **all** workspace folders
- Sorts them lexicographically to eliminate ordering dependencies
- Removes duplicates to handle edge cases
- Joins them with the `|` delimiter

The resulting string is then hashed to base-36 to generate compact keys for VS Code’s `workspaceState` storage. This ensures that `file:///project/a|file:///project/b` produces the same identifier as `file:///project/b|file:///project/a`, preventing data fragmentation when users rearrange workspace folders.

## Isolating State with Namespaced Persistence

All persisted data in SecureDesign is automatically scoped to the current workspace through the `WorkspaceStateService` class. Located in [`src/services/workspaceStateService.ts`](https://github.com/hbmartin/secure-design/blob/main/src/services/workspaceStateService.ts) (lines 76-89), the service's `get()` and `update()` methods automatically prefix keys with the hashed workspace ID generated by `getWorkspaceId()`.

This **namespacing** guarantees that settings, chat history, and cached design information remain isolated per workspace. When you switch between different multi-root configurations, SecureDesign retrieves only the data relevant to the currently active workspace identifier, preventing cross-contamination between projects.

## Per-Folder File Watching

File system monitoring in multi-root environments requires dedicated watchers for each root folder. The `FileWatcherService` in [`src/services/fileWatcherService.ts`](https://github.com/hbmartin/secure-design/blob/main/src/services/fileWatcherService.ts) (lines 86-122) implements this by creating a distinct `vscode.FileSystemWatcher` for every folder in the workspace.

Each watcher uses `vscode.RelativePattern` scoped specifically to its folder, ensuring that glob patterns like `**/*.html` only match files within that particular root. When file change events fire, the service includes the `workspaceName` property in the payload when more than one folder is present, allowing event handlers to distinguish between identically-named files in different roots.

## Detecting Workspace Changes

SecureDesign monitors structural changes to the workspace through the `vscode.workspace.onDidChangeWorkspaceFolders` event listener implemented in [`src/extension.ts`](https://github.com/hbmartin/secure-design/blob/main/src/extension.ts) (lines 73-88).

When folders are added or removed, the extension invokes `WorkspaceStateService.hasWorkspaceChanged()` to compare the newly computed workspace ID against the previously stored hash. If the workspace has changed—for instance, when adding a third folder to a two-folder workspace—the extension can trigger appropriate lifecycle actions such as resetting chat history, re-initializing file watchers, or updating UI state to reflect the new workspace composition.

## Tracking Origin in Design Files

The `DesignFile` type defined in [`src/types/designFile.ts`](https://github.com/hbmartin/secure-design/blob/main/src/types/designFile.ts) (lines 11-12) includes an optional `workspaceName` property. This metadata allows generated design mock-ups and secure documentation to carry context about their originating folder within a multi-root workspace.

When designs are created or loaded, this property enables the extension to link files back to their specific root folder, ensuring that operations like "reveal in explorer" target the correct location even when multiple folders contain similar file structures.

## Implementation Example

The following code demonstrates how SecureDesign integrates these mechanisms to handle both single and multi-root configurations:

```ts
// 1️⃣ Get the stable ID for the current workspace (single- or multi-root)
import { WorkspaceStateService } from './services/workspaceStateService';
const wsId = WorkspaceStateService.getInstance().getWorkspaceId();
// → "file:///c:/proj/a|file:///c:/proj/b" (sorted & hashed internally)

// 2️⃣ Store a value that is isolated per workspace
WorkspaceStateService.getInstance().update('chatHistory', historyArray);

// 3️⃣ File watcher that automatically includes the workspace name
import { FileWatcherService } from './services/fileWatcherService';
const watcher = new FileWatcherService();
watcher.setupWatcher({
  pattern: '**/*.html',
  onFileChange: ({ fileName, relativePath, workspaceName, changeType }) => {
    console.log(
      `🗂 ${workspaceName ?? 'single'} – ${changeType}: ${relativePath}`
    );
  },
});

```

## Summary

- **Stable Workspace IDs**: SecureDesign generates deterministic identifiers by sorting and hashing all folder URIs in multi-root workspaces, ensuring consistent state keys regardless of folder order.
- **Namespaced Storage**: All persisted data in `WorkspaceStateService` is automatically prefixed with the workspace ID, isolating settings and history per workspace configuration.
- **Dedicated File Watchers**: The `FileWatcherService` creates individual `FileSystemWatcher` instances for each root folder using `vscode.RelativePattern`, with events tagged by `workspaceName`.
- **Dynamic Change Detection**: The extension monitors `onDidChangeWorkspaceFolders` to detect structural changes and reset state when the workspace composition changes.
- **Workspace Metadata**: Design files carry optional `workspaceName` properties to maintain linkage with their originating folder in multi-root scenarios.

## Frequently Asked Questions

### How does SecureDesign generate unique identifiers for multi-root workspaces?

SecureDesign collects the URIs of all folders in the workspace, sorts them lexicographically to eliminate ordering dependencies, joins them with the `|` delimiter, and hashes the result to base-36. This deterministic approach ensures that the same set of folders always produces the same identifier in [`src/services/workspaceStateService.ts`](https://github.com/hbmartin/secure-design/blob/main/src/services/workspaceStateService.ts), regardless of the order in which they were added to the workspace.

### Does SecureDesign create separate file watchers for each folder in a workspace?

Yes. The `FileWatcherService` in [`src/services/fileWatcherService.ts`](https://github.com/hbmartin/secure-design/blob/main/src/services/fileWatcherService.ts) iterates through all workspace folders and creates a dedicated `vscode.FileSystemWatcher` for each one using `vscode.RelativePattern`. This ensures that file watching is scoped to individual roots, and change events include the `workspaceName` parameter to identify which folder triggered the event.

### What happens to chat history when workspace folders are added or removed?

When the workspace structure changes, SecureDesign detects the modification via `vscode.workspace.onDidChangeWorkspaceFolders` in [`src/extension.ts`](https://github.com/hbmartin/secure-design/blob/main/src/extension.ts) and compares the new workspace ID against the cached value using `hasWorkspaceChanged()`. If the workspace has changed, the extension can reset chat history, re-initialize watchers, and update UI state to ensure data integrity across different workspace configurations.

### How can design files identify which workspace folder they belong to?

The `DesignFile` interface defined in [`src/types/designFile.ts`](https://github.com/hbmartin/secure-design/blob/main/src/types/designFile.ts) includes an optional `workspaceName` property that stores the originating folder name. When designs are created in a multi-root workspace, this metadata is populated to link the file back to its specific root folder, enabling accurate navigation and context-aware operations even when multiple folders share similar file structures.