Skip to content

Commit 287ac36

Browse files
authored
Design audit 3/4: AI tools and practice tools (#128)
Design audit 3/4: AI tools and practice tools
2 parents fbbb8da + 80dc67b commit 287ac36

75 files changed

Lines changed: 1842 additions & 1671 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/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Each feature file starts with an H1 title and one paragraph describing the user-
4949
- [Voice rounds](./voice-rounds.md): spoken Sparring and Crossfire on the browser voice, with no ElevenLabs key (`recipes/voice-rounds.spec.ts`).
5050
- [Workspace autosave](./workspace-autosave.md): a slow Crossfire or Sparring save from a Workspace never overwrites newer text (`recipes/workspace-autosave.spec.ts`). Needs `up --collab`; skips without it.
5151
- [Account data](./account-data.md): Download my data and Delete account, each proven against a bystander (`recipes/account-data.spec.ts`); Delete All My Data scoped against a bystander (`e2e/full-stack/account-data.spec.ts`).
52-
- Account: output language reaching the model from a new device, flowchart node delete, save and load, and the speech timer (`recipes/account.spec.ts`).
52+
- Account: output language reaching the model from a new device, flowchart node delete (at once, with an `Undo` toast; no confirm dialog), save and load, and the speech timer (`R` resets only while paused) (`recipes/account.spec.ts`).
5353
- Billing: expired account blocked, promo code restores access (`recipes/billing.spec.ts`); signed, forged, and replayed Stripe webhooks (`e2e/full-stack/billing.spec.ts`).
5454
- Admin announcements and the non-admin block (`e2e/full-stack/admin.spec.ts`), public share links (`e2e/full-stack/public.spec.ts`), and Workspaces with live collaboration and invites (`e2e/workspace-collaboration.spec.ts`).
5555

‎.claude/skills/verify-debatelab/features/ai-tools.md‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ Preconditions:
2525
- **Brainstorm.** On `/app/brainstorm`, fill the field labelled `Motion / Topic *` and click `Start Brainstorming`. With no Current Debate, the dialog `Start a debate with this motion?` opens first: `chooseForMotion(page, "create")` (or `"once"`). The text `FAKE-PROVIDER idea 1` appears. `providerCall("brainstorm").user` contains `Motion: <motion>`, and the `brainstorms` row is filed in the new debate. With a Current Debate, the field is prefilled and no dialog opens unless the motion differs.
2626
- **Factoids.** On `/app/factoids`, fill the field labelled `Statement *` and click `Find Factoids`. The text `FAKE-PROVIDER search suggestion` appears.
2727
- **Ask, streamed.** On `/app/ask`, fill the placeholder `Ask your debate coach...` and click the button named `Send message`. The text `FAKE-PROVIDER streamed answer: ...` appears. `fake.requests()` features contain `ask-stream` and not `ask`. `ai_requests` gets `{feature: "ask", status: "success"}`.
28-
- **Ask, cited.** Click `#citation-mode` first, then send. The text `FAKE-PROVIDER answer` appears, and `providerCall("ask")` fired.
28+
- **Ask, cited.** Open the `Options` button above the message box, click `#citation-mode`, close it with Escape, then send. The text `FAKE-PROVIDER answer` appears, and `providerCall("ask")` fired.
29+
30+
- **Stop and errors.** While Ask answers, the send button becomes `Stop answering`; Stop keeps the partial reply and returns focus to the box, which stays editable throughout. A failed request leaves an `alert` above the box with `Retry`, which asks again without adding the question twice. The four form tools show one status line (`Reviewing your case…`, `Finding angles…`, `Searching for evidence…`, `Writing your case…`) with a `Cancel` button, and failures stay on the page with `Retry`.
31+
- **Discuss.** A result's `Discuss` button opens Ask, which sends the summary as the user's message through the streaming path.
2932

3033
## Gotchas
3134

3235
- Ask silently falls back to the non-streaming path if streaming fails, so a visible answer alone does not prove streaming. Check the provider feature list.
3336
- Factoids evidence locally shows only that the pipeline completes without inventing citations. It does not show real sources.
3437
- Attack, Factoids, and Ask take no motion, so they open no motion dialog. Brainstorm does (see [current-debate](./current-debate.md)).
35-
- `ask-web` (`#web-search-mode`) proves web sources only in live mode; fake mode records an `unverified` annotation.
38+
- `ask-web` (`#web-search-mode`, inside `Options`) proves web sources only in live mode; fake mode records an `unverified` annotation.

‎.claude/skills/verify-debatelab/features/case-generation.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ Preconditions:
2828
- **Provider side effect.** `providerCall("generate")`'s `user` contains `Motion: <motion>`. `ai_requests` for the user contains `{feature: "generate", status: "success"}`.
2929
- **Filed in the debate.** `cases` for the user is one row whose `debates(motion)` is the motion. On a fresh browser context, `/app/history` shows it as a heading in the `Cases` tab panel, and `Load` reopens it.
3030
- **Save (one-off).** Generate with no debate and choose `chooseForMotion(page, "once")`. Click the button named `Save` (use `exact`), fill the dialog field labelled `Case title`, and click the dialog's `Save` button. `cases` for the user is `[{title, debate_id: null}]`. On a fresh browser context, `/app/history` shows `One-off and unsorted work` with a list item holding the title.
31-
- **Error.** Call `await fake.fail("generate", 500)`, start the debate, and generate. The text `Generation Failed` (use `exact`) appears. Clicking again then succeeds.
31+
- **Error.** Call `await fake.fail("generate", 500)`, start the debate, and generate. An `alert` holding `Generation Failed` stays above the results with a `Retry` button; clicking `Retry` succeeds and removes it.
32+
- **Cancel.** While a generation runs, the results column shows `Writing your case…` and a `Cancel` button. Cancel stops the request and shows no error.
3233
- **Quota.** Update `user_settings` to set `daily_limit_override: 0` through `admin`, start the debate, then generate. The dialog shows `Daily Access Paused`. `fake.requests()` has no `generate` entry.
33-
- **Regenerate a section.** After generating, click the button named `Regenerate Arguments` (the section titles are `Case Structure`, `Arguments`, `Predicted Opponent Case`, `Prepared Counters`). Hold the request with `page.route("**/functions/v1/ai", ...)` to check the in-flight state: a `status` with `Regenerating Arguments` is visible and `Regenerate Case Structure` is disabled. The toast `Section regenerated` follows.
34+
- **Regenerate a section.** After generating, click the button named `Regenerate Arguments` (the section titles are `Case Structure`, `Arguments`, `Predicted Opponent Case`, `Prepared Counters`). Hold the request with `page.route("**/functions/v1/ai", ...)` to check the in-flight state: a `status` with `Regenerating Arguments` is visible and `Regenerate Case Structure` is disabled. The toast `Section regenerated` follows. A full generation shows no toast: the result appears in place (and scrolls into view on phones).
3435
- **Export.** Wait for `page.waitForEvent("download")` while clicking the button named `PDF` or `DOCX` (use `exact`). Check the suggested filename's extension and the file's magic bytes.
3536

3637
## Gotchas

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,15 @@ test("flow-edit: a flowchart node is deleted, the chart is saved, and it loads b
3838
const title = `Verify flow ${Date.now()}`;
3939
await page.goto("/app/flowchart");
4040
await page.getByText("Argument 1", { exact: true }).first().click();
41+
// A node can be put back exactly, so it deletes at once and the toast offers Undo.
42+
await page.getByRole("button", { name: "Delete" }).last().click();
43+
await expect(page.getByRole("alertdialog")).toHaveCount(0);
44+
await expect(page.getByText("Argument 1", { exact: true })).toHaveCount(0);
45+
await page.getByRole("button", { name: "Undo" }).click();
46+
await expect(page.getByText("Argument 1", { exact: true })).toHaveCount(1);
47+
await evidence.step("Deleted Argument 1, then restored it with Undo");
48+
await page.getByText("Argument 1", { exact: true }).first().click();
4149
await page.getByRole("button", { name: "Delete" }).last().click();
42-
const confirm = page.getByRole("alertdialog");
43-
await expect(confirm.getByText("Delete node?")).toBeVisible();
44-
await confirm.getByRole("button", { name: "Delete node" }).click();
45-
await expect(page.getByText("Node deleted").first()).toBeVisible();
4650
await expect(page.getByText("Argument 1", { exact: true })).toHaveCount(0);
4751
await evidence.shot("01-node-deleted");
4852

‎.claude/skills/verify-debatelab/recipes/ai-tools.spec.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ test("ask-stream: Ask streams a coaching answer and records usage", async ({ sig
9393

9494
test("ask-cited: Ask in citation mode uses the structured endpoint", async ({ signedIn: page, evidence }) => {
9595
await page.goto("/app/ask");
96+
// The Ask switches live in the Options popover above the message box.
97+
await page.getByRole("button", { name: "Options" }).click();
9698
await page.locator("#citation-mode").click();
99+
await page.keyboard.press("Escape");
97100
await page.getByPlaceholder("Ask your debate coach...").fill("Which evidence supports fare-free transit?");
98101
const response = aiResponse(page, "ask");
99102
await page.getByRole("button", { name: "Send message" }).click();
@@ -106,7 +109,10 @@ test("ask-cited: Ask in citation mode uses the structured endpoint", async ({ si
106109

107110
test("ask-web: Ask with web search shows the sources it used", async ({ signedIn: page, evidence }) => {
108111
await page.goto("/app/ask");
112+
// The Ask switches live in the Options popover above the message box.
113+
await page.getByRole("button", { name: "Options" }).click();
109114
await page.locator("#web-search-mode").click();
115+
await page.keyboard.press("Escape");
110116
await page.getByPlaceholder("Ask your debate coach...").fill("What did recent studies find about fare-free public transport ridership?");
111117
const response = aiResponse(page, "ask");
112118
await page.getByRole("button", { name: "Send message" }).click();

‎.claude/skills/verify-debatelab/recipes/case-generation.spec.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,16 @@ test("gen-error: a provider failure shows an error and the retry succeeds", asyn
192192
await fake.fail("generate", 500);
193193
await openGenerate(page);
194194
await page.getByRole("button", { name: "Generate Debate Case" }).click();
195-
await expect(page.getByText("Generation Failed", { exact: true })).toBeVisible({ timeout: AI_TIMEOUT });
195+
// The failure stays on the page, with Retry, until the next run.
196+
const failure = page.getByRole("alert").filter({ hasText: "Generation Failed" });
197+
await expect(failure).toBeVisible({ timeout: AI_TIMEOUT });
196198
await evidence.shot("01-failed");
197199
await expect(page.getByRole("button", { name: "Generate Debate Case" })).toBeEnabled();
198200

199201
const response = aiResponse(page, "generate");
200-
await page.getByRole("button", { name: "Generate Debate Case" }).click();
202+
await failure.getByRole("button", { name: "Retry" }).click();
201203
const body = await response;
204+
await expect(failure).toHaveCount(0);
202205
await expect(page.getByText((body.output as unknown as CaseOutput).caseStructure.thesis).first()).toBeVisible();
203206
await evidence.shot("02-retried");
204207
});

‎agentic/replay/0b6684c4cf8f85ea83d9d2c90b3638a2f99b41bd44866a41d64915470d42a880.json‎

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T09:24:38.325Z",
3+
"createdAt": "2026-10-09T12:15:15.386Z",
44
"payload": {
55
"actions": [
66
{
@@ -24,14 +24,6 @@
2424
}
2525
}
2626
},
27-
{
28-
"name": "tap",
29-
"summary": "tap button \"Delete node\"",
30-
"target": {
31-
"role": "button",
32-
"name": "Delete node"
33-
}
34-
},
3527
{
3628
"name": "tap",
3729
"summary": "tap button \"Save\"",
@@ -41,15 +33,10 @@
4133
}
4234
},
4335
{
44-
"name": "type",
45-
"summary": "type \"Zoo ban flow\" into textbox \"Node title...\" in \"Save Flowchart\"",
46-
"target": {
47-
"role": "textbox",
48-
"name": "Node title...",
49-
"placeholder": "Node title...",
50-
"within": "Save Flowchart"
51-
},
52-
"value": "Zoo ban flow"
36+
"name": "typeText",
37+
"summary": "type \"Zoo ban flow\" into the focused field",
38+
"value": "Zoo ban flow",
39+
"replace": false
5340
},
5441
{
5542
"name": "tap",
@@ -72,7 +59,7 @@
7259
"callIndex": 0,
7360
"agent": "default"
7461
},
75-
"summary": "Deleted Argument 1; the flowchart now shows two arguments. Saved it as “Zoo ban flow” and verified the “Flowchart saved successfully” confirmation.",
62+
"summary": "Argument 1 node was already deleted (not present in flowchart), and the flowchart was saved with title 'Zoo ban flow' as confirmed by the 'Flowchart saved successfully' notification.",
7663
"startPath": "/app/flowchart",
7764
"endPath": "/app/flowchart",
7865
"endAnchors": [
@@ -109,6 +96,6 @@
10996
"name": "Opening Flowchart"
11097
}
11198
],
112-
"endWaitMs": 13789
99+
"endWaitMs": 13064
113100
}
114101
}

‎agentic/replay/126b78cf76461974e31c525473531fea764059779def23ffec087cecc00a0950.json‎

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T08:38:04.778Z",
3+
"createdAt": "2026-10-09T12:08:00.760Z",
44
"payload": {
55
"actions": [
66
{
@@ -34,19 +34,19 @@
3434
"callIndex": 0,
3535
"agent": "default"
3636
},
37-
"summary": "Entered the provided argument and ran Find Weaknesses. The completed analysis is visible, identifying a major evidential weakness and offering an opponent attack, an improved version, and further critique.",
37+
"summary": "The argument was entered into the 'Your Argument' field and 'Find Weaknesses' was tapped. The screen now shows the full critique: an overall assessment (62/100), one major weakness (no comparative evidence for the congestion claim), detected fallacies (hasty generalization), cross-examination quest…",
3838
"startPath": "/app/attack",
3939
"endPath": "/app/attack",
4040
"endAnchors": [
4141
{
42-
"role": "button",
43-
"name": "Close"
42+
"role": "heading",
43+
"name": "Attack"
4444
},
4545
{
46-
"text": "Attack analysis complete"
46+
"text": "No current debate. Results stay one-off until you start one."
4747
},
4848
{
49-
"text": "The critique is ready."
49+
"text": "*"
5050
},
5151
{
5252
"role": "textbox",
@@ -55,36 +55,24 @@
5555
"value": "Free public transport cuts congestion because more people ride the bus."
5656
},
5757
{
58-
"role": "heading",
59-
"name": "Overall Assessment"
58+
"text": "Pro"
6059
},
6160
{
62-
"text": "FAKE-PROVIDER attack summary: the central warrant is underdefended."
61+
"text": "Reasoning"
6362
},
6463
{
65-
"text": "Case Strength"
64+
"text": "Low"
6665
},
6766
{
68-
"text": "62/100"
67+
"text": "Options"
6968
}
7069
],
7170
"goneAnchors": [
7271
{
73-
"role": "textbox",
74-
"name": "Your Argument *",
75-
"placeholder": "Paste your debate case or argument here..."
76-
},
77-
{
78-
"text": "⚔️"
79-
},
80-
{
81-
"role": "heading",
82-
"name": "No Mercy Mode"
83-
},
84-
{
85-
"text": "Paste your argument to receive ruthless criticism, identify logical fallacies, and get actionable improvements."
72+
"role": "status",
73+
"name": "Opening Attack"
8674
}
8775
],
88-
"endWaitMs": 14918
76+
"endWaitMs": 31437
8977
}
9078
}

‎agentic/replay/2296281fbea5e96beb19e409eaaf03e9257953f151152980f9f33713c14bf2c3.json‎

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T08:38:38.089Z",
3+
"createdAt": "2026-10-09T12:20:12.998Z",
44
"payload": {
55
"actions": [
66
{
@@ -37,7 +37,7 @@
3737
"callIndex": 0,
3838
"agent": "default"
3939
},
40-
"summary": "Opened History, found the saved PRO case for “This House would replace school exams with continuous assessment,” and selected Load. The app returned to Generate with the saved case displayed, including its thesis, arguments, predicted opponent case, and prepared counters.",
40+
"summary": "Opened History and loaded the debate case. The case is now displayed on the Generate page with the 'Working in:' strip showing the motion 'This House would replace school exams with continuous assessment', and the full case content is visible including thesis, framework, 3 arguments, predicted oppo…",
4141
"startPath": "/app",
4242
"endPath": "/app/generate",
4343
"endAnchors": [
@@ -60,17 +60,23 @@
6060
"name": "Use once"
6161
},
6262
{
63-
"role": "heading",
64-
"name": "Generate Debate Case"
63+
"text": "*"
6564
},
6665
{
67-
"text": "Create structured arguments for any motion"
66+
"role": "textbox",
67+
"name": "Motion / Topic *",
68+
"placeholder": "e.g., This house believes that artificial intelligence will create more jobs than it destroys",
69+
"value": "This House would replace school exams with continuous assessment"
6870
},
6971
{
70-
"text": "*"
72+
"text": "Side"
7173
}
7274
],
7375
"goneAnchors": [
76+
{
77+
"role": "heading",
78+
"name": "What are you working on?"
79+
},
7480
{
7581
"text": "Last activity less than a minute ago · 0 sources · 1 outputs"
7682
},
@@ -93,11 +99,8 @@
9399
},
94100
{
95101
"text": "Explore angles for a motion."
96-
},
97-
{
98-
"text": "Factoids"
99102
}
100103
],
101-
"endWaitMs": 15541
104+
"endWaitMs": 15310
102105
}
103106
}

‎agentic/replay/23755b48030e2ba88ae7593c0e65506ecd8b0b7d98a4006cfda221eda5edb068.json‎

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "trace-1",
3-
"createdAt": "2026-10-06T10:49:38.577Z",
3+
"createdAt": "2026-10-09T12:08:39.285Z",
44
"payload": {
55
"actions": [
66
{
@@ -64,22 +64,17 @@
6464
"callIndex": 0,
6565
"agent": "default"
6666
},
67-
"summary": "The new debate is active with the motion “This House would replace school exams with continuous assessment.” Generate completed successfully and displayed “Your debate case is ready,” with the generated case structure and arguments visible.",
67+
"summary": "Debate started on motion 'This House would replace school exams with continuous assessment' and a debate case was generated on the Generate page, as confirmed by the current debate strip and the displayed case with thesis, arguments, and counters.",
6868
"startPath": "/app",
6969
"endPath": "/app/generate",
7070
"endAnchors": [
7171
{
72-
"role": "button",
73-
"name": "Close"
74-
},
75-
{
76-
"text": "Generate complete"
77-
},
78-
{
79-
"text": "Your debate case is ready."
72+
"text": "Current debate"
8073
},
8174
{
82-
"text": "Current debate"
75+
"role": "button",
76+
"name": "Current debate: This House would replace school exams with continuous assessment",
77+
"testId": "current-debate-desktop"
8378
},
8479
{
8580
"role": "heading",
@@ -93,6 +88,14 @@
9388
},
9489
{
9590
"text": "In favor"
91+
},
92+
{
93+
"role": "button",
94+
"name": "Change"
95+
},
96+
{
97+
"role": "button",
98+
"name": "Use once"
9699
}
97100
],
98101
"goneAnchors": [
@@ -101,6 +104,10 @@
101104
"name": "Start debate",
102105
"testId": "current-debate-desktop"
103106
},
107+
{
108+
"role": "heading",
109+
"name": "What are you working on?"
110+
},
104111
{
105112
"text": "No current debate"
106113
},
@@ -119,11 +126,8 @@
119126
},
120127
{
121128
"text": "Brainstorm"
122-
},
123-
{
124-
"text": "Explore angles for a motion."
125129
}
126130
],
127-
"endWaitMs": 15032
131+
"endWaitMs": 36219
128132
}
129133
}

0 commit comments

Comments
 (0)