From 72b77566c50bdc461d3b8a96143c699d1e9fcf0e Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Mon, 21 Sep 2026 14:46:24 -0700 Subject: [PATCH] fix(web,harness): the demo's second view was the phantom improvise-m4rv removed CROSSES TWO BOUNDARIES OUTSIDE THIS AGENT'S GROUND, deliberately and reported: web/demo.improv and harness/tests/contract.spec.js. Dropping the invented Default view takes the shipped demo from two views to one, and the spec asserted the phantom in two places -- improvise-m4rv's acceptance criteria name the spec directly. The load-bearing half is the improvise-iioi click test, which needs the view panel to hold two rows so that a click can name a DIFFERENT row than the cursor does; with one view both readings give the same answer and the test stops discriminating. The phantom was supplying that second row by accident. So the demo gains a real second view, Season -- a genuine alternate pivot (Quarter by measure, paged by Product) rather than an auto-arrangement. Plan stays first and stays Initial View, so the cursor still starts at index 0 and tests/web_demo_model.rs is unaffected: every visible cell of the ACTIVE view is still a Record (gotcha 46). Verified by cargo. The spec's axis assertion at index 0 moves from the phantom's "R:Product C:Quarter" to Plan's own "R:Product P:Quarter", derived from viewmodel::view_axis_summary rather than guessed. NOT VERIFIED: harness/ has no node_modules and web/pkg is unbuilt here, so Playwright could not be run. These edits are mechanical substitutions and need a browser-job run before they are believed. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH --- harness/tests/contract.spec.js | 24 ++++++++++++------------ web/demo.improv | 8 ++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/harness/tests/contract.spec.js b/harness/tests/contract.spec.js index fc4c5f70..8da1d071 100644 --- a/harness/tests/contract.spec.js +++ b/harness/tests/contract.spec.js @@ -73,7 +73,7 @@ test.describe("every field the render model projects reaches the screen", () => // The axis line is the shared `view_axis_summary`; its absence would mean // a second derivation had crept in. await expect(page.locator("#views li").nth(0).locator(".axes")).toHaveText( - "R:Product C:Quarter", + "R:Product P:Quarter", ); await page.keyboard.press("Escape"); @@ -196,21 +196,21 @@ test.describe("a pointer event reaches the client and its answer is painted", () // A view row (improvise-iioi). The activation must read the CLICKED index, // not `view_panel_cursor` — so the cursor must not already be on the row - // clicked, or both readings give the same answer. `Default` is index 0 and - // holds the cursor by default, so move to it and click `Plan`. + // clicked, or both readings give the same answer. `Plan` is index 0 and + // holds the cursor by default, so move to it and click `Season`. await page.keyboard.press("Shift+V"); - await expect(page.locator("#views li.selected")).toContainText("Default"); - // Switch to Default by KEYBOARD, leaving the cursor on it. The click then + await expect(page.locator("#views li.selected")).toContainText("Plan"); + // Switch to Plan by KEYBOARD, leaving the cursor on it. The click then // names a different row than the cursor does, which is the whole point. await page.keyboard.press("Enter"); - await expect(page.locator("#view-name")).toHaveText("Default"); - - const plan = page.locator("#views li").filter({ hasText: "Plan" }).first(); - await plan.click(); - // Reading the cursor instead of the index re-selects Default and fails. await expect(page.locator("#view-name")).toHaveText("Plan"); - await expect(page.locator("#views li.active")).toContainText("Plan"); - await expect(page.locator("#views li.selected")).toContainText("Plan"); + + const season = page.locator("#views li").filter({ hasText: "Season" }).first(); + await season.click(); + // Reading the cursor instead of the index re-selects Plan and fails. + await expect(page.locator("#view-name")).toHaveText("Season"); + await expect(page.locator("#views li.active")).toContainText("Season"); + await expect(page.locator("#views li.selected")).toContainText("Season"); // A formula row: navigation only, because FormulaPanel binds no Enter and // a click mirrors Enter. The Rust guard is on the KEYMAP — clients.md. diff --git a/web/demo.improv b/web/demo.improv index b2eb845b..b1e14a28 100644 --- a/web/demo.improv +++ b/web/demo.improv @@ -10,6 +10,14 @@ _Index: none _Dim: none format: ,.0 +## View: Season +Quarter: row +_Measure: column +Product: page, Latte +_Index: none +_Dim: none +format: ,.0 + ## Formulas - Profit = Revenue - Cost