shell-redesign V1 attempt-3 Phase 2 BOUNCE-FIX #3: drawer width-coupling

guard-3 caught a real bug missed in bounce-fix #2: VellumSheet wide preset
was calibrated 45rem → 38rem, but AppShell.tsx workspaceRightOffset still
reserved 45rem of right padding via the --workspace-right-offset CSS
variable when drawer is open. Net effect: open-drawer surfaces a 7rem
(112px) layout gap between the drawer (38rem) and the right edge of the
center workspace's content.

Fix:
- AppShell.tsx workspaceRightOffset open-drawer literal "45rem" → "38rem".
- SharedDetailDrawer.tsx stale lg:w-[45rem] comment → lg:w-[38rem]
  (with calibration-date reference per content-drawer.md L9).
- New regression test in test/app-shell.test.tsx (CSS-source-assertion
  per pseudo-element-paint test contract / discipline ritual #7):
  * "VellumSheet wide preset and AppShell workspaceRightOffset use the
    SAME literal" — extracts widthClass.wide width from VellumSheet
    source and workspaceRightOffset open-drawer value from AppShell
    source via regex; asserts equal.
  * "no live 45rem string in chrome source" — scans AppShell, drawer,
    and vellum-sheet sources; for any line containing "45rem" verifies
    it's inside a // line comment OR a /* ... */ block comment context
    (historical calibration notes are allowed; live class/value strings
    are not). Permanent guard against the same coupling drift.

Verification:
- tsc --noEmit: clean
- pnpm test: 599/600 PASS (added 2 new tests; pre-existing
  test/library-review.test.tsx > Send-to-seat modal still failing
  unrelated to this scope; verified pre-Phase-1 via git stash)
- pnpm build: ✓ in 1.94s
- Runtime probe (drawer not mounted by default per SC-6) confirms
  static-source coupling test is the right approach; computed-style of
  CSS variables in jsdom is too brittle to depend on.

Permission preflight caveat acknowledged: bounce-fix #2 §6 fields
writable_root_coverage and approval_layer_probe were authored as
prose/boolean rather than the exact path+covered_by and command+result
subshape per velocity-permission-preflight schema. Orch accepted the
field-level close-out; will sharpen to the exact subshape on next
preflight authoring (likely Phase 3 dispatch).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
mvschwarzandClaude Opus 4.7 committed 2026-05-06 12:56:40 -07:00
1 parent a69275805d
commit c916c4bed6
3 files changed
+92 -4

No files matched your search

+5 -1
View File
@@ -374,7 +374,11 @@ export function AppShell({ children }: AppShellProps) {
const workspaceLeftOffset = isWideLayout
? `${3 + (explorerVisible && desktopExplorerOpen ? 18 : 0)}rem`
: "0rem";
const workspaceRightOffset = isWideLayout && drawerOpen ? "45rem" : "0rem";
// Coupled to VellumSheet wide preset (lg:w-[38rem]) — bounce-fix #3
// caught the gap that emerged when bounce-fix #2 calibrated the drawer
// 45rem → 38rem without updating this offset. Keep these two literals
// in sync; the regression test in app-shell.test.tsx asserts they match.
const workspaceRightOffset = isWideLayout && drawerOpen ? "38rem" : "0rem";
const workspaceStyle = {
"--workspace-left-offset": workspaceLeftOffset,
"--workspace-right-offset": workspaceRightOffset,
@@ -5,9 +5,10 @@
// right-sidebar (closer to document-reading width)."
//
// Phase 2 lays the chrome: VellumSheet primitive (Phase 1) wraps each
// panel; default-closed (selection === null returns null); ~720px wide
// on desktop (`lg:w-[45rem]` via VellumSheet width="wide"); user-closable
// via VellumSheet's onClose [×] button.
// panel; default-closed (selection === null returns null); ~608px wide
// on desktop (`lg:w-[38rem]` via VellumSheet width="wide"; calibrated
// 2026-05-06 per content-drawer.md L9); user-closable via VellumSheet's
// onClose [×] button.
//
// Phase 4 wires the new viewer types (QueueItemViewer, FileViewer,
// SubSpecPreview, SeatDetailViewer) and named triggers from feed cards
+83
View File
@@ -18,6 +18,8 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, cleanup, waitFor } from "@testing-library/react";
import { createMemoryHistory, RouterProvider, createRouter } from "@tanstack/react-router";
import { readFileSync } from "node:fs";
import path from "node:path";
import { createMockEventSourceClass } from "./helpers/mock-event-source.js";
const mockFetch = vi.fn();
@@ -248,6 +250,87 @@ describe("AppShell — Phase 2 chrome", () => {
});
});
// Phase 2 BOUNCE-FIX #3 — width-coupling regression (guard-3 catch).
// The center workspace's --workspace-right-offset CSS variable must equal
// the VellumSheet wide preset width when drawer is open. Bounce-fix #2
// calibrated VellumSheet 45rem → 38rem but missed this consumer; net
// effect was a 7rem (112px) gap between drawer and reserved padding.
// Per pseudo-element-paint test contract (discipline ritual #7), assert
// via CSS source rather than runtime (computed style of CSS vars from
// jsdom is brittle).
describe("Drawer width / right-offset coupling (bounce-fix #3 regression)", () => {
const APP_SHELL_SRC = readFileSync(
path.resolve(__dirname, "../src/components/AppShell.tsx"),
"utf8",
);
const VELLUM_SHEET_SRC = readFileSync(
path.resolve(__dirname, "../src/components/ui/vellum-sheet.tsx"),
"utf8",
);
const SHARED_DRAWER_SRC = readFileSync(
path.resolve(__dirname, "../src/components/SharedDetailDrawer.tsx"),
"utf8",
);
it("VellumSheet wide preset and AppShell workspaceRightOffset use the SAME literal", () => {
// Pull the wide-preset width from VellumSheet source.
const vellumMatch = VELLUM_SHEET_SRC.match(
/wide:\s*"w-full\s+lg:w-\[(\d+rem)\]/,
);
expect(vellumMatch, "VellumSheet wide preset must declare lg:w-[Xrem]").toBeTruthy();
const vellumWide = vellumMatch![1];
// Pull the open-drawer offset from AppShell source.
const offsetMatch = APP_SHELL_SRC.match(
/workspaceRightOffset\s*=\s*[^?]*\?\s*"(\d+rem)"\s*:/,
);
expect(offsetMatch, "AppShell workspaceRightOffset must declare ternary 'Xrem' : '0rem'")
.toBeTruthy();
const offsetOpen = offsetMatch![1];
expect(offsetOpen, "AppShell workspaceRightOffset must equal VellumSheet wide preset width")
.toBe(vellumWide);
});
it("no live 45rem string in chrome source (only historical calibration comments are allowed)", () => {
// Extract every line containing "45rem" and verify each is inside
// a comment (calibration history). Chrome source must NOT carry
// 45rem as a live class or value.
const checkSource = (src: string, label: string) => {
const lines = src.split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!line.includes("45rem")) continue;
// Permitted only when the line is a JS/TS line comment ("//") or
// an active block-comment context ("/*", "*"). We scan backward
// for a recent /* opener if no "//" on this line.
const trimmed = line.trim();
const isLineComment = trimmed.startsWith("//") || trimmed.startsWith("*");
let isInsideBlockComment = false;
if (!isLineComment) {
// Look backward up to 30 lines for a /* without an intervening */.
for (let j = i - 1; j >= Math.max(0, i - 30); j--) {
if (lines[j].includes("*/")) break;
if (lines[j].includes("/*")) {
isInsideBlockComment = true;
break;
}
}
}
expect(
isLineComment || isInsideBlockComment,
`${label}:${i + 1} contains live (non-comment) "45rem" — bounce-fix #3 width-coupling regression`,
).toBe(true);
}
};
checkSource(APP_SHELL_SRC, "AppShell.tsx");
checkSource(SHARED_DRAWER_SRC, "SharedDetailDrawer.tsx");
// VellumSheet keeps a historical calibration comment with 45rem;
// it's inside a // comment so the same checker passes there too.
checkSource(VELLUM_SHEET_SRC, "vellum-sheet.tsx");
});
});
describe("Legacy buttons removed (Phase 2 deleted Sidebar + header toggle pattern)", () => {
it("specs-toggle button does NOT exist", async () => {
const { container } = await renderAt("/");