Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add server-side workspaceRoot uniqueness invariant - #1614

Closed
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness
Closed

Add server-side workspaceRoot uniqueness invariant#1614
jonsing wants to merge 2 commits into
pingdotgg:mainfrom
jonsing:feat/server-side-workspace-root-uniqueness

Conversation

@jonsing

@jonsingjonsing commented Mar 31, 2026

Copy link
Copy Markdown

Duplicate workspaceRoot detection previously only happened client-side in Sidebar.tsx. This adds a server-side invariant in the orchestration decider that rejects project.create and project.meta.update commands when the target workspaceRoot is already used by another non-deleted project. Soft-deleted projects are excluded from the check. Reason for this change is reported bug #1595

What Changed

In total 46 additions, the additional 198 additions are test code.

Adds a helper function in apps/server/src/orchestration/commandInvariants.ts to easily find existing projects given a workspaceRoot. Only used within requireWorkspaceRootUnique function so one could argue that this is unnecessary abstraction at this point, happy to change this to be inline.

exportfunctionfindProjectByWorkspaceRoot(readModel: OrchestrationReadModel,workspaceRoot: string,): OrchestrationProject|undefined{returnreadModel.projects.find((project)=>project.workspaceRoot===workspaceRoot&&project.deletedAt===null,);}

Adds a function that check if there is already a project with the workspaceRoot property persited. If so return a Effect.fail else void.

exportfunctionrequireWorkspaceRootUnique(input: {readonlyreadModel: OrchestrationReadModel;readonlycommand: OrchestrationCommand;readonlyworkspaceRoot: string;readonlyexcludeProjectId?: ProjectId;}): Effect.Effect<void,OrchestrationCommandInvariantError>{constexisting=findProjectByWorkspaceRoot(input.readModel,input.workspaceRoot);if(!existing||existing.id===input.excludeProjectId){returnEffect.void;}returnEffect.fail(invariantError(input.command.type,`Workspace root '${input.workspaceRoot}' is already used by project '${existing.id}'.`,),);}

In the apps/server/src/orchestration/decider.ts code I imported the new requireWorkspaceRootUnique function and used it inside the project.create and project.meta.update case handlers to verify that project with same workspaceRoot does not already exist.

Added relevant new test to assert the checks are working as intended. All existing test pass after change.

Why

Server code should have the same validations as the client code has to avoid duplicate project creation (same workspaceRoot) in the db. Either this check should be done in the two handlers changed in this PR or the db schema should enforce the workspaceRoot to be unique.

I would argue that this is a good first step to avoid the issue expressed in #1595 to occur and then if the db should enforce this (which is a much bigger change) db migrations would most likely be added to handle "bad" client state.

UI Changes

No UI changes have been made.

Checklist

  • This PR is small and focused
  • I explained what changed and why

Note

Medium Risk
Adds new server-side validation that can cause project.create and project.meta.update commands to start failing for previously-allowed duplicate workspaceRoot values; impact is limited to orchestration command handling and covered by new tests.

Overview
Enforces server-side uniqueness of workspaceRoot across active projects. The orchestration decider now rejects project.create and project.meta.update (when changing workspaceRoot) if the path is already used by another non-deleted project.

Adds findProjectByWorkspaceRoot/requireWorkspaceRootUnique invariants (including an excludeProjectId self-update case) and expands unit/integration tests to cover conflicts, soft-deleted projects, and edge cases with pre-existing duplicates.

Written by Cursor Bugbot for commit 983f1bb. This will update automatically on new commits. Configure here.

Note

Add server-side uniqueness invariant for workspaceRoot on project create and update

  • Adds requireWorkspaceRootUnique in commandInvariants.ts that scans non-deleted projects and rejects with an OrchestrationCommandInvariantError if the given workspaceRoot is already in use.
  • Integrates the check into decider.ts for both project.create and project.meta.update; the update path excludes the current project from the check to allow self-retention.
  • Also adds findProjectByWorkspaceRoot, a helper that returns the first non-deleted project matching a given path.
  • Behavioral Change: project.create and project.meta.update commands now fail with an invariant error when another non-deleted project already uses the requested workspaceRoot.

Macroscope summarized 983f1bb.

Duplicate workspaceRoot detection previously only happened client-side
in Sidebar.tsx. This adds a server-side invariant in the orchestration
decider that rejects project.create and project.meta.update commands
when the target workspaceRoot is already used by another non-deleted
project. Soft-deleted projects are excluded from the check.
@coderabbitai

coderabbitaiBot commented Mar 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 10214698-3ce3-451f-abdf-84331018293d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actionsgithub-actionsBot added size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Mar 31, 2026
Comment threadapps/server/src/orchestration/commandInvariants.ts
Move the excludeProjectId filter into the search predicate so the
lookup skips the excluded project entirely instead of finding the first
match and comparing after the fact.

@cursorcursorBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

): OrchestrationProject | undefined {
return readModel.projects.find(
(project) => project.workspaceRoot === workspaceRoot && project.deletedAt === null,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Unused helper duplicates workspace lookup logic

Low Severity

findProjectByWorkspaceRoot is exported but not used by runtime code, and requireWorkspaceRootUnique reimplements the same workspaceRoot lookup inline. This creates dead API surface and duplicated logic in commandInvariants.ts, increasing the chance these checks drift apart later.

Additional Locations (1)
Fix in CursorFix in Web

@juliusmarminge

Copy link
Copy Markdown
Member

Closing as superseded by merged PR #3829, which landed the current workspace-root uniqueness behavior.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M30-99 changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@jonsing@juliusmarminge@martin-ptsoft