Skip to content

Commit fbbb8da

Browse files
authored
Design audit 2/4: Shell (#126)
Design audit 2/4: shell (wayfinding, one delete rule, pending states, page headers)
2 parents bba0666 + f08cdad commit fbbb8da

70 files changed

Lines changed: 1621 additions & 1087 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/skills/verify-debatelab/features/library-sources.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ Preconditions:
2222

2323
- Doctor passes. The user is signed in (`signedIn` fixture). A Current Debate exists: `await startDebate(page, motion)`. Without one, `Add Source` stays disabled.
2424

25-
- **Add.** Click the button named `Add Source`. Fill the textbox named `Source title...`, fill the textbox named `Paste source content...` with a unique marker, and click `Save to Library`. The switch named `Toggle <title>` is checked. `sources` has a row with `is_active: true` and the debate's `debate_id`.
25+
- **Add.** Click the button named `Add Source`. Fill the textbox named `Title` (exact), fill the textbox named `Content` (exact) with a unique marker, and click `Save to Library`. Leaving either empty shows an inline error under the field and moves focus to it. The switch named `Toggle <title>` is checked. `sources` has a row with `is_active: true` and the debate's `debate_id`.
2626
- **Toggle.** Click the switch named `Toggle <title>`. It becomes not checked.
2727
- **Ground.** Go to `/app/brainstorm`; the motion is prefilled from the debate. Run it (see [ai-tools](./ai-tools.md)). `providerCall("brainstorm").system` contains the active marker and does not contain the paused marker.
28-
- **Delete.** Click the button named `Delete <title>` (use `exact`). The switch count drops to 0, and stays 0 after `page.reload()`. `sources` no longer has the title.
29-
- **Import.** In the add panel, fill `Import from URL` and click `Import URL`. `Source Ready` appears, then `Save imported source` stores a row whose `origin` is the URL.
28+
- **Delete.** Click the button named `Delete <title>` (use `exact`). It deletes at once, with no confirmation, and a toast `Deleted <title>` offers `Undo`, which saves the source again. The switch count drops to 0, and stays 0 after `page.reload()`. `sources` no longer has the title.
29+
- **Import.** In the add panel, fill `Import from URL` and click `Import URL`. The `Import preview` panel appears, then `Save imported source` stores a row whose `origin` is the URL.
3030
- **Erase.** On Settings, click `Delete All My Data`. In the dialog, fill `Type your email to confirm` with `user.email` and click the dialog's `Delete All My Data`. The text `Data Deleted` appears, and `sources` for the user is `[]`.
3131

3232
## Gotchas

‎.claude/skills/verify-debatelab/recipes/account-data.spec.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const accountUrl = `${process.env.LOCAL_SUPABASE_URL}/functions/v1/account`;
1212
async function addSource(page: Page, title: string, content: string) {
1313
await page.goto("/app/library");
1414
await page.getByRole("button", { name: "Add Source" }).click();
15-
await page.getByRole("textbox", { name: "Source title..." }).fill(title);
16-
await page.getByRole("textbox", { name: "Paste source content..." }).fill(content);
15+
await page.getByRole("textbox", { name: "Title", exact: true }).fill(title);
16+
await page.getByRole("textbox", { name: "Content", exact: true }).fill(content);
1717
await page.getByRole("button", { name: "Save to Library" }).click();
1818
await expect(page.getByRole("switch", { name: `Toggle ${title}` })).toBeChecked();
1919
}

‎.claude/skills/verify-debatelab/recipes/current-debate.spec.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ const LIVE_AWARE_TIMEOUT = LIVE ? 900_000 : 150_000;
1414
async function addSource(page: Page, title: string, content: string) {
1515
await page.goto("/app/library");
1616
await page.getByRole("button", { name: "Add Source" }).click();
17-
await page.getByRole("textbox", { name: "Source title..." }).fill(title);
18-
await page.getByRole("textbox", { name: "Paste source content..." }).fill(content);
17+
await page.getByRole("textbox", { name: "Title", exact: true }).fill(title);
18+
await page.getByRole("textbox", { name: "Content", exact: true }).fill(content);
1919
await page.getByRole("button", { name: "Save to Library" }).click();
2020
await expect(page.getByRole("switch", { name: `Toggle ${title}` })).toBeChecked();
2121
}

‎.claude/skills/verify-debatelab/recipes/library.spec.ts‎

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ const motion = "Public transport should be free";
77

88
async function addSource(page: Page, title: string, content: string) {
99
await page.getByRole("button", { name: "Add Source" }).click();
10-
await page.getByRole("textbox", { name: "Source title..." }).fill(title);
11-
await page.getByRole("textbox", { name: "Paste source content..." }).fill(content);
10+
await page.getByRole("textbox", { name: "Title", exact: true }).fill(title);
11+
await page.getByRole("textbox", { name: "Content", exact: true }).fill(content);
1212
await page.getByRole("button", { name: "Save to Library" }).click();
1313
await expect(page.getByRole("switch", { name: `Toggle ${title}` })).toBeChecked();
1414
}
@@ -37,13 +37,28 @@ test("lib-add, lib-toggle, lib-ground, lib-delete: only active sources ground AI
3737
await evidence.json("02-grounding", { activeInPrompt: true, pausedInPrompt: false });
3838

3939
await page.goto("/app/library");
40+
const userId = await user.userId();
41+
const pausedRow = async () =>
42+
((await admin.from("sources").select("id,created_at,is_active").eq("user_id", userId).eq("title", "Paused study")).data ?? [])
43+
// The client keeps milliseconds; Postgres keeps microseconds.
44+
.map((row) => ({ id: row.id, at: Math.floor(Date.parse(row.created_at)), active: row.is_active }));
45+
const before = await pausedRow();
46+
expect(before).toHaveLength(1);
47+
// Deleting a source acts at once; the toast's Undo puts the same row back (same id, so a
48+
// per-request exclusion still applies, and the same date, so it keeps its place).
49+
await page.getByRole("button", { name: "Delete Paused study", exact: true }).click();
50+
await expect(page.getByRole("switch", { name: "Toggle Paused study" })).toHaveCount(0);
51+
await expect.poll(pausedRow).toEqual([]);
52+
await page.getByRole("button", { name: "Undo" }).click();
53+
await expect(page.getByRole("switch", { name: "Toggle Paused study" })).toHaveCount(1);
54+
await expect.poll(pausedRow).toEqual(before);
55+
await evidence.step("Deleted Paused study, then restored the same row with Undo");
4056
await page.getByRole("button", { name: "Delete Paused study", exact: true }).click();
4157
await expect(page.getByRole("switch", { name: "Toggle Paused study" })).toHaveCount(0);
4258
await page.reload();
4359
await expect(page.getByRole("switch", { name: "Toggle Active study" })).toBeVisible();
4460
await expect(page.getByRole("switch", { name: "Toggle Paused study" })).toHaveCount(0);
4561
// Live research can save the web sources it found, so check only the two added here.
46-
const userId = await user.userId();
4762
const { data: debate } = await admin.from("debates").select("id").eq("user_id", userId).eq("motion", motion).single();
4863
const { data } = await admin.from("sources").select("title,is_active,debate_id").eq("user_id", userId).in("title", ["Active study", "Paused study"]);
4964
expect(data).toEqual([{ title: "Active study", is_active: true, debate_id: debate?.id }]);
@@ -58,7 +73,7 @@ test("lib-import: a web page imports into the Library", async ({ signedIn: page,
5873
await page.getByRole("button", { name: "Add Source" }).click();
5974
await page.getByRole("textbox", { name: "Import from URL" }).fill(url);
6075
await page.getByRole("button", { name: "Import URL" }).click();
61-
await expect(page.getByText("Source Ready", { exact: true })).toBeVisible({ timeout: AI_TIMEOUT });
76+
await expect(page.getByText("Import preview", { exact: true })).toBeVisible({ timeout: AI_TIMEOUT });
6277
await evidence.shot("01-import-ready");
6378
await page.getByRole("button", { name: "Save imported source" }).click();
6479
await expect.poll(async () => {

‎agentic/replay/c6cf102fb2172d14552e85bf19abb2d80a6075794717cc7c0c2cd0610bb9fe37.json‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T08:42:12.424Z",
3+
"createdAt": "2026-10-09T07:51:18.781Z",
44
"payload": {
55
"actions": [
66
{
@@ -54,20 +54,20 @@
5454
},
5555
{
5656
"name": "type",
57-
"summary": "type \"Plastic bag levy study\" into textbox \"Source title...\"",
57+
"summary": "type \"Plastic bag levy study\" into textbox \"Title\"",
5858
"target": {
5959
"role": "textbox",
60-
"name": "Source title...",
60+
"name": "Title",
6161
"placeholder": "Source title..."
6262
},
6363
"value": "Plastic bag levy study"
6464
},
6565
{
6666
"name": "type",
67-
"summary": "type \"After a 5p levy, single-use bag sales a…\" into textbox \"Paste source content...\"",
67+
"summary": "type \"After a 5p levy, single-use bag sales a…\" into textbox \"Content\"",
6868
"target": {
6969
"role": "textbox",
70-
"name": "Paste source content...",
70+
"name": "Content",
7171
"placeholder": "Paste source content..."
7272
},
7373
"value": "After a 5p levy, single-use bag sales at major retailers fell by more than 95 percent."
@@ -93,26 +93,21 @@
9393
"callIndex": 0,
9494
"agent": "default"
9595
},
96-
"summary": "Started the debate “This House would ban single-use plastics” and added “Plastic bag levy study” to the Library with the provided content. The Library shows the saved source and a “Source Added” confirmation.",
96+
"summary": "Started debate on 'This House would ban single-use plastics', opened Library, and added source 'Plastic bag levy study' with content about 5p levy reducing bag sales by 95%. Source now appears in library with 1 source active and grounding enabled.",
9797
"startPath": "/app",
9898
"endPath": "/app/library",
9999
"endAnchors": [
100-
{
101-
"role": "button",
102-
"name": "Close"
103-
},
104-
{
105-
"text": "Source Added"
106-
},
107-
{
108-
"text": "Your source is now available for grounding."
109-
},
110100
{
111101
"text": "Current debate"
112102
},
113103
{
114104
"text": "This House would ban single-use plastics"
115105
},
106+
{
107+
"role": "button",
108+
"name": "Current debate: This House would ban single-use plastics",
109+
"testId": "current-debate-mobile"
110+
},
116111
{
117112
"role": "heading",
118113
"name": "Source Library"
@@ -122,7 +117,14 @@
122117
"name": "Change"
123118
},
124119
{
125-
"text": "All folders"
120+
"text": "1 sources · 1 active · 1 folders"
121+
},
122+
{
123+
"text": "Smart"
124+
},
125+
{
126+
"role": "heading",
127+
"name": "Your Knowledge Base"
126128
}
127129
],
128130
"goneAnchors": [
@@ -131,6 +133,10 @@
131133
"name": "Start debate",
132134
"testId": "current-debate-mobile"
133135
},
136+
{
137+
"role": "heading",
138+
"name": "What are you working on?"
139+
},
134140
{
135141
"text": "No current debate"
136142
},
@@ -149,11 +155,8 @@
149155
},
150156
{
151157
"text": "Brainstorm"
152-
},
153-
{
154-
"text": "Explore angles for a motion."
155158
}
156159
],
157-
"endWaitMs": 12722
160+
"endWaitMs": 13153
158161
}
159162
}

‎agentic/replay/cc6903d8aafb702ef5a496a05b9c45338886c52fc41ea6a7c0f6fd1435dfbfec.json‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T08:52:21.869Z",
3+
"createdAt": "2026-10-09T07:48:57.404Z",
44
"payload": {
55
"actions": [
66
{
@@ -32,7 +32,7 @@
3232
"callIndex": 0,
3333
"agent": "default"
3434
},
35-
"summary": "Changed the Interface Language setting to Español. The settings page and navigation are now displayed in Spanish, including “Ajustes” and “Idioma de Interfaz.”.",
35+
"summary": "The interface language has been switched to Español. The settings page now displays entirely in Spanish (Ajustes, Herramientas, Biblioteca, Historial, Idioma de Interfaz), and the combobox shows Español as the selected value. The language switch took effect successfully.",
3636
"startPath": "/app/settings",
3737
"endPath": "/app/settings",
3838
"endAnchors": [
@@ -41,7 +41,7 @@
4141
"name": "Logotipo de DebateLab"
4242
},
4343
{
44-
"text": "Inicio"
44+
"text": "Herramientas"
4545
},
4646
{
4747
"role": "button",
@@ -72,7 +72,7 @@
7272
"name": "DebateLab logo"
7373
},
7474
{
75-
"text": "Home"
75+
"text": "Tools"
7676
},
7777
{
7878
"role": "button",
@@ -97,6 +97,6 @@
9797
"name": "Settings"
9898
}
9999
],
100-
"endWaitMs": 17439
100+
"endWaitMs": 32208
101101
}
102102
}

‎agentic/replay/d15dccac4ac18fdabdb7d4c086d281513bd8832a317f7d3d65ac987fec417dc4.json‎

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T08:52:18.268Z",
3+
"createdAt": "2026-10-09T07:52:16.070Z",
44
"payload": {
55
"actions": [
66
{
@@ -32,17 +32,14 @@
3232
"callIndex": 0,
3333
"agent": "default"
3434
},
35-
"summary": "Changed the Interface Language setting to Español. The settings page now displays Spanish labels, including “Idioma de Interfaz” with “Español” selected.",
35+
"summary": "The interface language has been switched to Español. The settings page now displays all labels in Spanish (Ajustes, Idioma de Interfaz, Suscripción, Cuenta, Zona de peligro, etc.) and the Interface Language combobox shows Español as the selected value.",
3636
"startPath": "/app/settings",
3737
"endPath": "/app/settings",
3838
"endAnchors": [
3939
{
4040
"role": "image",
4141
"name": "Logotipo de DebateLab"
4242
},
43-
{
44-
"text": "Cambiar tema"
45-
},
4643
{
4744
"role": "button",
4845
"name": "Iniciar debate",
@@ -56,51 +53,52 @@
5653
"role": "heading",
5754
"name": "Suscripción"
5855
},
59-
{
60-
"text": "Pro"
61-
},
6256
{
6357
"text": "El acceso a la IA está activo. Pueden aplicarse límites de uso."
6458
},
6559
{
6660
"role": "heading",
6761
"name": "Ajustes globales"
62+
},
63+
{
64+
"text": "Idioma de Interfaz"
65+
},
66+
{
67+
"text": "Español"
6868
}
6969
],
7070
"goneAnchors": [
71-
{
72-
"role": "status",
73-
"name": "Opening Settings"
74-
},
7571
{
7672
"role": "image",
7773
"name": "DebateLab logo"
7874
},
79-
{
80-
"text": "Toggle theme"
81-
},
8275
{
8376
"role": "button",
8477
"name": "Start debate",
8578
"testId": "current-debate-mobile"
8679
},
8780
{
88-
"role": "link",
89-
"name": "Terms"
81+
"role": "heading",
82+
"name": "Settings"
9083
},
9184
{
92-
"role": "link",
93-
"name": "Privacy"
85+
"role": "heading",
86+
"name": "Subscription"
9487
},
9588
{
96-
"role": "link",
97-
"name": "DebateLab logo DebateLab"
89+
"text": "AI access is active. Usage limits may apply."
9890
},
9991
{
100-
"role": "button",
101-
"name": "Language"
92+
"role": "heading",
93+
"name": "Global Settings"
94+
},
95+
{
96+
"text": "Interface Language"
97+
},
98+
{
99+
"text": "AI Output Language"
102100
}
103101
],
104-
"endWaitMs": 12650
102+
"endWaitMs": 28157
105103
}
106104
}

0 commit comments

Comments
 (0)