summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-16 21:30:36 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-16 21:30:36 +0000
commit1b6b27357c817359574605b854f6468917da314d (patch)
tree1e03e989fa91a09bc8c93b8727ad454baa6e7e12
parent0e4cd564d4c3819f487e4b7469c410d485e42dec (diff)
fix: hide deployment badge when not deployed and trim notification button label
- Deployment badge now returns null (hidden) when includes_fix is false instead of showing "Not deployed" noise - Badge also suppressed when fix_commits is empty (no tracked commits to check) - Notification button label trimmed to just the bell emoji - Preamble: warn agents not to use absolute paths in git commands (sandbox bypass) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--internal/executor/preamble.go1
-rw-r--r--internal/executor/preamble_test.go7
-rw-r--r--web/app.js10
-rw-r--r--web/index.html2
-rw-r--r--web/test/deployment-badge.test.mjs24
5 files changed, 23 insertions, 21 deletions
diff --git a/internal/executor/preamble.go b/internal/executor/preamble.go
index f5dba2b..b949986 100644
--- a/internal/executor/preamble.go
+++ b/internal/executor/preamble.go
@@ -45,6 +45,7 @@ The sandbox is rejected if there are any uncommitted modifications.
- One commit is fine. Multiple focused commits are also fine.
- If you realise the task was already done and you made no changes, that is also fine — just exit cleanly without committing.
- Do not exit with uncommitted edits.
+- **CRITICAL:** Run ALL git commands from your current directory — do NOT use absolute paths or "cd <project_path> && git ...". Your working directory IS the project. Using absolute paths bypasses the sandbox and breaks commit tracking.
---
diff --git a/internal/executor/preamble_test.go b/internal/executor/preamble_test.go
index 984f786..5c31b4f 100644
--- a/internal/executor/preamble_test.go
+++ b/internal/executor/preamble_test.go
@@ -22,3 +22,10 @@ func TestPlanningPreamble_SummaryInstructsEchoToFile(t *testing.T) {
t.Error("planningPreamble should show example of writing to $CLAUDOMATOR_SUMMARY_FILE via echo")
}
}
+
+func TestPlanningPreamble_GitDiscipline_ForbidsAbsolutePaths(t *testing.T) {
+ // Agents must not bypass the sandbox by using absolute project paths in git commands.
+ if !strings.Contains(planningPreamble, "do NOT use absolute paths") {
+ t.Error("planningPreamble should warn agents not to use absolute paths in git commands")
+ }
+}
diff --git a/web/app.js b/web/app.js
index 73f5a5c..dca4472 100644
--- a/web/app.js
+++ b/web/app.js
@@ -107,8 +107,7 @@ export function renderDeploymentBadge(status, doc = (typeof document !== 'undefi
span.className = 'deployment-badge deployment-badge--deployed';
span.textContent = '✓ Deployed';
} else {
- span.className = 'deployment-badge deployment-badge--pending';
- span.textContent = '⚠ Not deployed';
+ return null;
}
if (status.deployed_commit) {
span.title = `Deployed commit: ${status.deployed_commit.slice(0, 8)}`;
@@ -179,8 +178,9 @@ function createTaskCard(task) {
if (csBadge) card.appendChild(csBadge);
}
- // Deployment status badge for READY tasks
- if (task.state === 'READY' && task.deployment_status != null) {
+ // Deployment status badge for READY tasks — only when there are tracked commits to check.
+ if (task.state === 'READY' && task.deployment_status != null &&
+ task.deployment_status.fix_commits && task.deployment_status.fix_commits.length > 0) {
const depBadge = renderDeploymentBadge(task.deployment_status);
if (depBadge) card.appendChild(depBadge);
}
@@ -2660,7 +2660,7 @@ async function enableNotifications(btn) {
if (!res.ok) throw new Error(`Subscribe failed: HTTP ${res.status}`);
if (btn) {
- btn.textContent = '🔔 On';
+ btn.textContent = '🔔';
btn.disabled = true;
}
} catch (err) {
diff --git a/web/index.html b/web/index.html
index 64dd486..c17601b 100644
--- a/web/index.html
+++ b/web/index.html
@@ -17,7 +17,7 @@
<option value="claude">Claude</option>
<option value="gemini">Gemini</option>
</select>
- <button id="btn-notifications" class="btn-secondary" title="Enable push notifications">🔔 Notifications</button>
+ <button id="btn-notifications" class="btn-secondary" title="Enable push notifications">🔔</button>
<button id="btn-start-next" class="btn-secondary">Start Next</button>
<button id="btn-new-task" class="btn-primary">New Task</button>
</div>
diff --git a/web/test/deployment-badge.test.mjs b/web/test/deployment-badge.test.mjs
index afb4a59..438fb27 100644
--- a/web/test/deployment-badge.test.mjs
+++ b/web/test/deployment-badge.test.mjs
@@ -33,8 +33,14 @@ describe('renderDeploymentBadge', () => {
assert.equal(el, null);
});
- it('returns element with deployment-badge class for valid status', () => {
- const status = { deployed_commit: 'abc123', fix_commits: [], includes_fix: false };
+ it('returns null when includes_fix is false', () => {
+ const status = { deployed_commit: 'abc123', fix_commits: [{ hash: 'aabbcc', message: 'fix' }], includes_fix: false };
+ const el = renderDeploymentBadge(status, makeDoc());
+ assert.equal(el, null);
+ });
+
+ it('returns element with deployment-badge class when includes_fix is true', () => {
+ const status = { deployed_commit: 'abc123', fix_commits: [{ hash: 'aabbcc', message: 'fix' }], includes_fix: true };
const el = renderDeploymentBadge(status, makeDoc());
assert.ok(el, 'element should not be null');
assert.ok(el.className.includes('deployment-badge'), `className should include deployment-badge, got: ${el.className}`);
@@ -46,26 +52,14 @@ describe('renderDeploymentBadge', () => {
assert.ok(el.textContent.includes('Deployed'), `expected "Deployed" in "${el.textContent}"`);
});
- it('shows "Not deployed" text when includes_fix is false', () => {
- const status = { deployed_commit: 'abc123', fix_commits: [{ hash: 'aabbcc', message: 'fix' }], includes_fix: false };
- const el = renderDeploymentBadge(status, makeDoc());
- assert.ok(el.textContent.includes('Not deployed'), `expected "Not deployed" in "${el.textContent}"`);
- });
-
it('applies deployed class when includes_fix is true', () => {
const status = { deployed_commit: 'abc123', fix_commits: [{ hash: 'aabbcc', message: 'fix' }], includes_fix: true };
const el = renderDeploymentBadge(status, makeDoc());
assert.ok(el.className.includes('deployment-badge--deployed'), `className: ${el.className}`);
});
- it('applies pending class when includes_fix is false', () => {
- const status = { deployed_commit: 'abc123', fix_commits: [{ hash: 'aabbcc', message: 'fix' }], includes_fix: false };
- const el = renderDeploymentBadge(status, makeDoc());
- assert.ok(el.className.includes('deployment-badge--pending'), `className: ${el.className}`);
- });
-
it('returns null for doc=null', () => {
- const status = { deployed_commit: 'abc', fix_commits: [], includes_fix: false };
+ const status = { deployed_commit: 'abc', fix_commits: [], includes_fix: true };
const el = renderDeploymentBadge(status, null);
assert.equal(el, null);
});