-
+
@@ -445,6 +456,7 @@ function EditServerModal({
key={c}
onClick={() => setColor(c)}
disabled={isWarRoom}
+ aria-label={`Set server color to ${c}`}
className={`w-8 h-8 rounded-lg bg-gradient-to-br border ${COLOR_MAP[c]} ${
color === c ? "ring-2 ring-white/40 ring-offset-2 ring-offset-[#0d0d0f]" : ""
} disabled:opacity-40 disabled:cursor-not-allowed`}
diff --git a/components/settings-modal.tsx b/components/settings-modal.tsx
index 87776d0..200f331 100644
--- a/components/settings-modal.tsx
+++ b/components/settings-modal.tsx
@@ -59,7 +59,11 @@ export function SettingsModal({
Settings
-
+
@@ -656,6 +660,7 @@ function ProfileEditor({
onClick={() => setIconUrl("")}
className={`relative w-11 h-11 rounded-full overflow-hidden border-2 bg-neutral-950 ${iconUrl === "" ? "border-amber-400" : "border-neutral-800 hover:border-neutral-700"}`}
title="Built-in"
+ aria-label="Use built-in logo"
>
{adapter.defaultIconUrl ? (
// eslint-disable-next-line @next/next/no-img-element
@@ -670,6 +675,7 @@ function ProfileEditor({
key={p.url}
onClick={() => setIconUrl(p.url)}
title={p.label}
+ aria-label={`Use ${p.label} logo`}
className={`w-11 h-11 rounded-full overflow-hidden border-2 bg-neutral-950 ${iconUrl === p.url ? "border-amber-400" : "border-neutral-800 hover:border-neutral-700"}`}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
@@ -702,6 +708,7 @@ function ProfileEditor({
key={opt.value}
onClick={() => setAccent(opt.value)}
title={opt.label}
+ aria-label={`Use ${opt.label} accent color`}
className={`w-7 h-7 rounded-full border-2 ${opt.swatch} ${accent === opt.value ? "border-white" : "border-transparent hover:border-neutral-700"}`}
/>
))}
diff --git a/tests/icon-button-labels.test.ts b/tests/icon-button-labels.test.ts
new file mode 100644
index 0000000..cc0554a
--- /dev/null
+++ b/tests/icon-button-labels.test.ts
@@ -0,0 +1,47 @@
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import fs from "node:fs";
+import path from "node:path";
+
+function read(relPath: string): string {
+ return fs.readFileSync(path.join(process.cwd(), relPath), "utf8");
+}
+
+test("issue #8 icon-only buttons have explicit aria-labels", () => {
+ const channelHeader = read("components/channel-header.tsx");
+ assert.match(channelHeader, /title=\{title\}\s+aria-label=\{title\}/, "channel header icon buttons should reuse title as aria-label");
+
+ const rail = read("components/rail.tsx");
+ for (const label of [
+ 'aria-label="Create server"',
+ 'aria-label="Invite teammates"',
+ 'aria-label="Sync status"',
+ 'aria-label="Settings"',
+ 'aria-label={`Open ${server.name} server`}',
+ 'aria-label="Close create server dialog"',
+ 'aria-label="Close edit server dialog"',
+ 'aria-label={`Set server color to ${c}`}',
+ ]) {
+ assert.ok(rail.includes(label), `rail is missing ${label}`);
+ }
+
+ const channelList = read("components/channel-list.tsx");
+ for (const label of [
+ 'aria-label={`Create channel in ${g.label}`}',
+ 'aria-label={`Delete category "${g.label}"`}',
+ 'aria-label="Cancel new category"',
+ 'aria-label="Channel options"',
+ ]) {
+ assert.ok(channelList.includes(label), `channel list is missing ${label}`);
+ }
+
+ const settingsModal = read("components/settings-modal.tsx");
+ for (const label of [
+ 'aria-label="Close settings"',
+ 'aria-label="Use built-in logo"',
+ 'aria-label={`Use ${p.label} logo`}',
+ 'aria-label={`Use ${opt.label} accent color`}',
+ ]) {
+ assert.ok(settingsModal.includes(label), `settings modal is missing ${label}`);
+ }
+});
From 324a9a297f9f092395f437febd30d7f48bf8b41b Mon Sep 17 00:00:00 2001
From: Codex
Date: Mon, 1 Jun 2026 19:52:09 +0200
Subject: [PATCH 2/2] test: align OpenWar opt-in migration expectation
---
README.md | 2 +-
tests/migration.test.ts | 13 +++++++++----
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/README.md b/README.md
index ec79721..7525056 100644
--- a/README.md
+++ b/README.md
@@ -62,7 +62,7 @@ Full walkthrough per mode: [`docs/sync-hosting.md`](./docs/sync-hosting.md). Syn
War Room ships with [**OpenWar**](https://github.com/pythonluvr/openwar) as the bundled default agent framework. OpenWar is a system prompt that makes any agent (Claude, GPT, Gemini, custom CLI) behave like a senior peer: confirms briefs before acting, breaks work into phases, asks before destructive actions, refuses to invent next steps not grounded in the brief.
-The framework is opt-in per channel and globally. The default selection lives in `system_settings.default_framework`; new installs are seeded to `openwar`. Frameworks are plain markdown files at `presets/frameworks/*.md`; drop a new one in and it shows up automatically in the wizard picker and channel header chip. No registration code, no manifest.
+The framework is opt-in per channel and globally. The default selection lives in `system_settings.default_framework`; new installs leave it unset until you opt in. Frameworks are plain markdown files at `presets/frameworks/*.md`; drop a new one in and it shows up automatically in the wizard picker and channel header chip. No registration code, no manifest.
Update bundled frameworks from upstream:
diff --git a/tests/migration.test.ts b/tests/migration.test.ts
index fc6606a..aa35d6e 100644
--- a/tests/migration.test.ts
+++ b/tests/migration.test.ts
@@ -172,13 +172,13 @@ test("migrate adds adapter_id + agent_id + new seeds without losing legacy rows"
);
}
- // Cold-clone seeds default.framework=openwar. Legacy installs that
- // hadn't seen this seed before get it added now (no existing setting
- // row → seedDefaultFramework inserts).
+ // OpenWar is opt-in/default-off on cold-clone installs now. Legacy
+ // installs that never set a framework should stay unset instead of
+ // getting the old default-on seed reintroduced by migration.
const fw = db.prepare(`SELECT value FROM settings WHERE key = ?`).get("default.framework") as
| { value: string }
| undefined;
- assert.equal(fw?.value, "openwar", "default.framework setting should be seeded to openwar");
+ assert.equal(fw, undefined, "default.framework should stay unset until the user opts in");
} finally {
cleanup();
}
@@ -237,6 +237,11 @@ test("fresh DB (no legacy tables) gets full schema from cold", () => {
assert.ok(warRoom, "cold-clone DB should have War Room seeded");
const personal = db.prepare(`SELECT * FROM user_servers WHERE is_personal = 1`).get();
assert.ok(personal, "cold-clone DB should have a personal workspace seeded");
+
+ // Behavioral overlays are explicit opt-ins. A new database should not
+ // silently enable OpenWar for users who already bring their own prompt.
+ const fw = db.prepare(`SELECT value FROM settings WHERE key = ?`).get("default.framework");
+ assert.equal(fw, undefined, "cold-clone DB should not auto-enable OpenWar");
} finally {
cleanup();
}