From bac6d296ddf7e24f067f4f641af1cb7721aea25d Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:27:56 -0700 Subject: [PATCH 1/7] chore(porch): bugfix-766 init bugfix --- .../status.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml diff --git a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml new file mode 100644 index 00000000..00685128 --- /dev/null +++ b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml @@ -0,0 +1,12 @@ +id: bugfix-766 +title: bug-architect-pane-shrinks-to- +protocol: bugfix +phase: investigate +plan_phases: [] +current_plan_phase: null +gates: {} +iteration: 1 +build_complete: false +history: [] +started_at: '2026-05-19T00:27:56.031Z' +updated_at: '2026-05-19T00:27:56.032Z' From 43959a4067985d50192df717773b537295cb5e33 Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:37:30 -0700 Subject: [PATCH 2/7] chore(porch): bugfix-766 fix phase-transition --- .../bugfix-766-bug-architect-pane-shrinks-to-/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml index 00685128..3290772f 100644 --- a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml +++ b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml @@ -1,7 +1,7 @@ id: bugfix-766 title: bug-architect-pane-shrinks-to- protocol: bugfix -phase: investigate +phase: fix plan_phases: [] current_plan_phase: null gates: {} @@ -9,4 +9,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-19T00:27:56.031Z' -updated_at: '2026-05-19T00:27:56.032Z' +updated_at: '2026-05-19T00:37:30.337Z' From 613270662ef77cd5ad49713215656a75412e8316 Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:37:48 -0700 Subject: [PATCH 3/7] [Bugfix #766] Add missing CSS for .architect-pane / .architect-pane-body PR #762's N>1 left-pane branch wraps the architect terminal in two new divs (.architect-pane and .architect-pane-body) but never added matching CSS. The wrappers collapsed to intrinsic block height and the architect terminal rendered at ~1/4 of the SplitPane left side for multi-architect workspaces. The N=1 path renders directly so the existing unit-test DOM-snapshot check did not catch it. Fix: - index.css: .architect-pane is now a flex-direction:column host with height:100% min-height:0; .architect-pane-body has flex:1 min-height:0 so it fills the remaining space below the tab strip. - architect-pane-layout.test.ts: Playwright regression test that mocks /api/state with N=2 architects and asserts the architect-pane fills .split-left vertically and architect-pane-body reaches the bottom. Fixes #766 --- .../e2e/architect-pane-layout.test.ts | 96 +++++++++++++++++++ packages/dashboard/src/index.css | 15 +++ 2 files changed, 111 insertions(+) create mode 100644 packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts diff --git a/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts b/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts new file mode 100644 index 00000000..48989991 --- /dev/null +++ b/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts @@ -0,0 +1,96 @@ +/** + * Bugfix #766: regression guard for the multi-architect (N>1) left pane layout. + * + * PR #762 introduced `.architect-pane` / `.architect-pane-body` wrappers in the + * N>1 branch of `App.tsx` but never added matching CSS, so the architect + * terminal collapsed to ~1/4 of the SplitPane left side. The fix in + * `packages/dashboard/src/index.css` makes `.architect-pane` a `flex-direction: + * column` host with `height: 100%` and `.architect-pane-body` a `flex: 1` + * filler. This test pins the layout invariant by mocking `/api/state` with + * N=2 architects and asserting the architect-pane-body fills almost all of the + * SplitPane left side minus the tab strip. + * + * Prerequisites: + * - Tower running on TOWER_TEST_PORT (default 4100) + * - npx playwright install chromium + * + * Run: npx playwright test architect-pane-layout + */ + +import { test, expect } from '@playwright/test'; +import { resolve } from 'node:path'; + +const TOWER_URL = `http://localhost:${process.env.TOWER_TEST_PORT || '4100'}`; +const WORKSPACE_PATH = resolve(import.meta.dirname, '../../../../../../'); +const ENCODED_PATH = Buffer.from(WORKSPACE_PATH).toString('base64url'); +const DASH_URL = `${TOWER_URL}/workspace/${ENCODED_PATH}/`; + +test.describe('Bugfix #766: multi-architect pane fills SplitPane left side', () => { + test('N=2 architect-pane-body fills the SplitPane left side', async ({ page }) => { + // Mock /api/state with two architects so the N>1 branch in App.tsx renders + // the `.architect-pane` / `.architect-pane-body` wrappers. + await page.route('**/api/state', async (route) => { + const response = await route.fetch(); + const base = response.ok() ? await response.json().catch(() => ({})) : {}; + const mainArchitect = { name: 'main', port: 0, pid: 1, terminalId: 'term-766-main', persistent: false }; + const siblingArchitect = { name: 'sibling-766', port: 0, pid: 2, terminalId: 'term-766-sibling', persistent: false }; + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + ...base, + architect: mainArchitect, + architects: [mainArchitect, siblingArchitect], + builders: [], + utils: [], + annotations: [], + }), + }); + }); + + await page.goto(DASH_URL); + await page.locator('#root').waitFor({ state: 'attached', timeout: 10_000 }); + + // Confirm the N>1 branch rendered: architect tab strip is visible. + const tabStrip = page.locator('[aria-label="Architect tabs"]'); + await expect(tabStrip).toBeVisible({ timeout: 10_000 }); + + const splitLeft = page.locator('.split-left'); + const architectPane = page.locator('.architect-pane'); + const architectBody = page.locator('.architect-pane-body'); + + await expect(splitLeft).toBeVisible(); + await expect(architectPane).toBeVisible(); + await expect(architectBody).toBeAttached(); + + // The architect-pane wrapper should fill the full height of the SplitPane + // left side. Allow a small delta for borders / sub-pixel rounding. + const [leftBox, paneBox, stripBox, bodyBox] = await Promise.all([ + splitLeft.boundingBox(), + architectPane.boundingBox(), + tabStrip.boundingBox(), + architectBody.boundingBox(), + ]); + + expect(leftBox, '.split-left must have a bounding box').not.toBeNull(); + expect(paneBox, '.architect-pane must have a bounding box').not.toBeNull(); + expect(stripBox, 'architect tab strip must have a bounding box').not.toBeNull(); + expect(bodyBox, '.architect-pane-body must have a bounding box').not.toBeNull(); + + // .architect-pane fills .split-left vertically (within 2px tolerance). + expect(Math.abs(paneBox!.height - leftBox!.height)).toBeLessThanOrEqual(2); + + // .architect-pane-body fills the remaining space below the tab strip. + // Pre-fix, the body collapsed to ~0px (children-only intrinsic height + // since `Terminal` had no fixed height inside an undefined-height flex + // child). Post-fix it must be at least 60% of the left pane height. + const expectedMinBodyHeight = (leftBox!.height - stripBox!.height) * 0.6; + expect(bodyBox!.height).toBeGreaterThanOrEqual(expectedMinBodyHeight); + + // And the body's bottom should reach (within 2px) the bottom of the + // split-left container — i.e. the pane is not collapsed to ~1/4 height. + const leftBottom = leftBox!.y + leftBox!.height; + const bodyBottom = bodyBox!.y + bodyBox!.height; + expect(Math.abs(leftBottom - bodyBottom)).toBeLessThanOrEqual(2); + }); +}); diff --git a/packages/dashboard/src/index.css b/packages/dashboard/src/index.css index 4648fe13..8fae8565 100644 --- a/packages/dashboard/src/index.css +++ b/packages/dashboard/src/index.css @@ -307,6 +307,21 @@ body { font-size: 14px; } +/* Bugfix #766: multi-architect left-pane wrapper (Spec 761 N>1 path). + * Without these rules the wrappers collapse to intrinsic block height, + * leaving the architect terminal at ~1/4 of the SplitPane left side. */ +.architect-pane { + display: flex; + flex-direction: column; + height: 100%; + min-height: 0; +} + +.architect-pane-body { + flex: 1; + min-height: 0; +} + /* Virtual keyboard for mobile terminals (Issue #254) */ .virtual-keyboard { display: flex; From d4dad7687c3d3173a8556d4e241549324f29df9a Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:38:18 -0700 Subject: [PATCH 4/7] chore(porch): bugfix-766 pr phase-transition --- .../bugfix-766-bug-architect-pane-shrinks-to-/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml index 3290772f..898c1eb2 100644 --- a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml +++ b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml @@ -1,7 +1,7 @@ id: bugfix-766 title: bug-architect-pane-shrinks-to- protocol: bugfix -phase: fix +phase: pr plan_phases: [] current_plan_phase: null gates: {} @@ -9,4 +9,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-19T00:27:56.031Z' -updated_at: '2026-05-19T00:37:30.337Z' +updated_at: '2026-05-19T00:38:18.602Z' From e94b514ffef9d2e810200f34cb7d66abdea847f5 Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:55:39 -0700 Subject: [PATCH 5/7] [Bugfix #766] iter-2: position .architect-pane absolutely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first iteration used height: 100% on .architect-pane, which relies on the entire ancestor chain (.app-body in particular) participating in a deterministic height context. .app-body has no explicit display, so its flex item children inherit the default block-level height behavior; in that case 100% can resolve to 0 when the flex container's main-axis size hasn't propagated yet. Switch to position: absolute / inset: 0 against .split-left (which is already position: relative). This sidesteps the percentage-height plumbing entirely — the pane just fills its positioned ancestor. --- packages/dashboard/src/index.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/dashboard/src/index.css b/packages/dashboard/src/index.css index 8fae8565..5ba2128d 100644 --- a/packages/dashboard/src/index.css +++ b/packages/dashboard/src/index.css @@ -309,11 +309,15 @@ body { /* Bugfix #766: multi-architect left-pane wrapper (Spec 761 N>1 path). * Without these rules the wrappers collapse to intrinsic block height, - * leaving the architect terminal at ~1/4 of the SplitPane left side. */ + * leaving the architect terminal at ~1/4 of the SplitPane left side. + * `.split-left` already sets `position: relative`, so positioning the pane + * absolutely against it sidesteps any flex/percentage-height plumbing in + * ancestor containers. */ .architect-pane { + position: absolute; + inset: 0; display: flex; flex-direction: column; - height: 100%; min-height: 0; } From 367bc4896b40181b6597bc8270b9ead35531b022 Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:58:43 -0700 Subject: [PATCH 6/7] [Bugfix #766] Update test JSDoc to match iter-2 (position:absolute approach) --- .../agent-farm/__tests__/e2e/architect-pane-layout.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts b/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts index 48989991..6ed45ac1 100644 --- a/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts +++ b/packages/codev/src/agent-farm/__tests__/e2e/architect-pane-layout.test.ts @@ -4,8 +4,9 @@ * PR #762 introduced `.architect-pane` / `.architect-pane-body` wrappers in the * N>1 branch of `App.tsx` but never added matching CSS, so the architect * terminal collapsed to ~1/4 of the SplitPane left side. The fix in - * `packages/dashboard/src/index.css` makes `.architect-pane` a `flex-direction: - * column` host with `height: 100%` and `.architect-pane-body` a `flex: 1` + * `packages/dashboard/src/index.css` makes `.architect-pane` a + * `position: absolute; inset: 0` flex column anchored against `.split-left` + * (which is `position: relative`), with `.architect-pane-body` as a `flex: 1` * filler. This test pins the layout invariant by mocking `/api/state` with * N=2 architects and asserting the architect-pane-body fills almost all of the * SplitPane left side minus the tab strip. From 786f208437ce1c44310f3c1eed9eeca1b441d077 Mon Sep 17 00:00:00 2001 From: M Waleed Kadous Date: Mon, 18 May 2026 17:59:00 -0700 Subject: [PATCH 7/7] chore(porch): bugfix-766 protocol complete --- .../bugfix-766-bug-architect-pane-shrinks-to-/status.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml index 898c1eb2..f6ceb4bf 100644 --- a/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml +++ b/codev/projects/bugfix-766-bug-architect-pane-shrinks-to-/status.yaml @@ -1,7 +1,7 @@ id: bugfix-766 title: bug-architect-pane-shrinks-to- protocol: bugfix -phase: pr +phase: verified plan_phases: [] current_plan_phase: null gates: {} @@ -9,4 +9,4 @@ iteration: 1 build_complete: false history: [] started_at: '2026-05-19T00:27:56.031Z' -updated_at: '2026-05-19T00:38:18.602Z' +updated_at: '2026-05-19T00:59:00.634Z'