Skip to content

Commit 4cf1d38

Browse files
Address brad's feedback
1 parent 4dcdd25 commit 4cf1d38

3 files changed

Lines changed: 49 additions & 14 deletions

File tree

DEPLOYMENT.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pip install gunicorn # Linux / macOS
1212
# pip install waitress # Windows-friendly alternative (see below)
1313

1414
# Multi-process (recommended): one thread per worker avoids any per-worker state surprises.
15-
gunicorn --factory --bind 127.0.0.1:3000 --workers 2 --threads 1 app:create_app
15+
# WEB_CONCURRENCY (or CURSOR_BROWSER_MULTI_WORKER=1) lets the app detect multi-worker mode so
16+
# POST /api/set-workspace returns 409 instead of a misleading 200 on a single worker.
17+
WEB_CONCURRENCY=2 gunicorn --factory --bind 127.0.0.1:3000 --workers 2 --threads 1 app:create_app
1618

1719
# Single-process, multi-threaded: safe after the #43 lock; useful for lighter deployments.
1820
gunicorn --factory --bind 127.0.0.1:3000 --workers 1 --threads 4 app:create_app

templates/config.html

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,35 @@ <h1>Configuration</h1>
1818
</div>
1919

2020
<script>
21+
async function postSetWorkspace(path) {
22+
const res = await fetch('/api/set-workspace', {
23+
method: 'POST',
24+
headers: { 'Content-Type': 'application/json' },
25+
body: JSON.stringify({ path })
26+
});
27+
let body = {};
28+
try {
29+
body = await res.json();
30+
} catch (e) {
31+
body = {};
32+
}
33+
if (res.status === 409) {
34+
return {
35+
ok: false,
36+
message: body.error || 'Workspace path cannot be changed in this deployment.',
37+
code: body.code
38+
};
39+
}
40+
if (!res.ok) {
41+
return {
42+
ok: false,
43+
message: body.error || 'Failed to save workspace path.',
44+
code: body.code
45+
};
46+
}
47+
return { ok: true, body };
48+
}
49+
2150
document.addEventListener('DOMContentLoaded', async () => {
2251
const stored = localStorage.getItem('workspacePath');
2352
if (stored) {
@@ -53,14 +82,12 @@ <h1>Configuration</h1>
5382
});
5483
const valData = await valRes.json();
5584
if (valData.valid) {
56-
localStorage.setItem('workspacePath', detected);
57-
await fetch('/api/set-workspace', {
58-
method: 'POST',
59-
headers: { 'Content-Type': 'application/json' },
60-
body: JSON.stringify({ path: detected })
61-
});
62-
window.location.href = '/';
63-
return;
85+
const saved = await postSetWorkspace(detected);
86+
if (saved.ok) {
87+
localStorage.setItem('workspacePath', detected);
88+
window.location.href = '/';
89+
return;
90+
}
6491
}
6592
}
6693
document.getElementById('workspace-path').value = detected;
@@ -84,12 +111,14 @@ <h1>Configuration</h1>
84111
const data = await res.json();
85112

86113
if (data.valid) {
114+
const saved = await postSetWorkspace(path);
115+
if (!saved.ok) {
116+
statusEl.className = 'alert alert-danger';
117+
statusEl.textContent = saved.message;
118+
statusEl.style.display = 'block';
119+
return;
120+
}
87121
localStorage.setItem('workspacePath', path);
88-
await fetch('/api/set-workspace', {
89-
method: 'POST',
90-
headers: { 'Content-Type': 'application/json' },
91-
body: JSON.stringify({ path })
92-
});
93122
statusEl.className = 'alert alert-success';
94123
statusEl.textContent = `Found ${data.workspaceCount} workspaces in the specified location`;
95124
statusEl.style.display = 'block';

tests/test_set_workspace_multiworker.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ def test_multi_worker_returns_409_with_stable_code(self):
4242
self.assertEqual(body["code"], "set_workspace_multi_worker_unsupported")
4343
self.assertIn("WORKSPACE_PATH", body["error"])
4444

45+
from utils.workspace_path import get_workspace_path_override
46+
47+
self.assertIsNone(get_workspace_path_override())
48+
4549
def test_single_process_still_succeeds_when_not_multi_worker(self):
4650
with patch(
4751
"api.config_api.is_multi_worker_process_deployment",

0 commit comments

Comments
 (0)