Skip to content

Commit eb5a1cd

Browse files
fix: prevent workspace overwrites. (#100)
1 parent 5c916c9 commit eb5a1cd

44 files changed

Lines changed: 8399 additions & 6056 deletions

Some content is hidden

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

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Repository structure:
5050
- In Playwright tests, prefer accessible selectors first: `getByRole`, `getByLabel`, `getByText`, and explicit accessible names.
5151
- Avoid `locator()` for interactive controls when a semantic selector is available.
5252
- Use `locator()` only as a fallback for cases without reliable semantics (for example: document root `html`, structural class assertions, or implementation-only hooks).
53+
- For known WebKit HTML `<dialog>` top-layer issues, prefer a stable dialog id locator and `evaluate`-based click for dialog confirmation controls.
5354
- When testability needs improvement, prefer adding accessibility semantics (`role`, `aria-label`, `aria-labelledby`) over introducing new id-only selectors.
5455

5556
## CDN and runtime expectations

docs/localstorage-state.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,17 @@ This document is the source of truth for what `@knighted/develop` stores in `loc
88

99
1. `knighted:develop:github-pat`
1010
- GitHub personal access token used for API calls.
11-
2. `knighted:develop:github-repository`
12-
- Last selected repository full name (for example: `owner/repo`).
13-
3. `knighted-develop:render-mode`
11+
2. `knighted-develop:render-mode`
1412
- Last selected render mode (`dom` or `react`).
15-
4. Theme/UI preference keys managed by layout theme modules.
13+
3. Theme/UI preference keys managed by layout theme modules.
1614

1715
## Not Allowed In localStorage
1816

1917
Do not store pull request context in `localStorage`.
2018

2119
Examples that must stay out of `localStorage`:
2220

21+
- Selected repository preference (`owner/repo`)
2322
- PR context state (`active`, `disconnected`, `closed`, `inactive`)
2423
- PR number and URL
2524
- PR base/head/title/body
@@ -31,3 +30,5 @@ Examples that must stay out of `localStorage`:
3130
`localStorage` is for lightweight bootstrap preferences only.
3231

3332
If data is needed to restore workspace or pull request workflow state, it belongs in IndexedDB workspace records.
33+
34+
Repository selection is derived from in-memory BYOT controls and IndexedDB-backed workspace records, not from a dedicated localStorage key.

docs/playwright-testing.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Playwright Testing Notes
2+
3+
## WebKit and HTML Dialog Overlays
4+
5+
WebKit can be sensitive to Playwright actionability checks when interacting with
6+
HTML `<dialog>` overlays. In some flows, role-based or standard click actions can
7+
time out even when controls are visibly rendered and usable.
8+
9+
Use this fallback pattern for dialog confirmation flows when WebKit flakes:
10+
11+
1. Target the dialog by stable id (for example: `#clear-confirm-dialog`) instead
12+
of a broad `getByRole('dialog')` selector.
13+
2. Use `evaluate`-based click for submit/confirm controls inside the dialog.
14+
3. Scope text assertions to the dialog locator to avoid matching background UI.
15+
16+
Keep accessible selectors as the default in tests. Use this dialog fallback only
17+
for known WebKit top-layer interaction issues.

docs/pr-context-storage-matrix.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,22 @@ Use this matrix as the source of truth when debugging UI/storage mismatch.
4343

4444
## Current Workspace Selection On Load
4545

46-
When the app loads or the selected repository changes, the app selects a workspace from IndexedDB using repository-scoped records only.
46+
When the app loads, workspace restore scope depends on whether a repository is selected.
47+
48+
- If a repository is selected: use repository-scoped records only (`repo` match).
49+
- If no repository is selected: evaluate all stored workspace records.
4750

4851
Selection order:
4952

50-
1. Load records for the currently selected repository (`repo` match).
51-
2. Compute a preferred id from in-memory state:
53+
1. Load candidate records using the scope above.
54+
2. Compute preferred candidates from in-memory state:
5255

53-
- Existing in-memory active record id when available.
54-
- Otherwise canonical id derived from current repository + head.
56+
- Preferred by id: existing in-memory active record id when available.
57+
- Preferred by workspace key: current repository + head (`workspaceKey`).
5558

56-
3. If the preferred record exists and is `active`, select it.
57-
4. Otherwise select the first `active` record in that repository.
58-
5. Otherwise select the preferred record if present.
59+
3. If preferred-by-id or preferred-by-key exists and is `active`, select it.
60+
4. Otherwise select the first `active` record in candidates.
61+
5. Otherwise select preferred-by-id or preferred-by-key if present.
5962
6. Otherwise fall back to the first record returned by IDB ordering.
6063

6164
Notes:

playwright/diagnostics.spec.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
runTypecheck,
1212
setComponentEditorSource,
1313
setStylesEditorSource,
14+
waitForLintDiagnosticsIssues,
1415
waitForInitialRender,
1516
} from './helpers/app-test-helpers.js'
1617

@@ -337,9 +338,7 @@ test('component lint reports missing button type prop', async ({ page }) => {
337338

338339
await runComponentLint(page)
339340

340-
await expect(page.getByText(/Rendered \(Lint issues: [1-9]\d*\)/)).toBeVisible()
341-
await ensureDiagnosticsDrawerOpen(page)
342-
await expect(page.getByText('Biome reported issues.')).toBeVisible()
341+
await waitForLintDiagnosticsIssues(page)
343342
await expect(page.getByText(/a11y\/useButtonType/)).toBeVisible()
344343
})
345344

@@ -354,10 +353,7 @@ test('styles diagnostics rows navigate editor to reported line', async ({ page }
354353

355354
await runStylesLint(page)
356355

357-
await expect(page.getByRole('button', { name: /^Diagnostics/ })).toHaveClass(
358-
/diagnostics-toggle--error/,
359-
)
360-
await ensureDiagnosticsDrawerOpen(page)
356+
await waitForLintDiagnosticsIssues(page)
361357

362358
const targetDiagnostic = page.getByRole('button', { name: /^L3(:\d+)?\s/ }).first()
363359
await expect(targetDiagnostic).toBeVisible()
@@ -375,9 +371,10 @@ test('styles lint reports CSS syntax errors', async ({ page }) => {
375371

376372
await runStylesLint(page)
377373

378-
await expect(page.getByText(/Rendered \(Lint issues: [1-9]\d*\)/)).toBeVisible()
379-
await ensureDiagnosticsDrawerOpen(page)
380-
await expect(page.getByText('Biome reported issues.')).toBeVisible()
374+
await waitForLintDiagnosticsIssues(page)
375+
await expect(page.locator('#diagnostics-styles')).toContainText(
376+
'Biome reported issues.',
377+
)
381378
})
382379

383380
test('sass compiler warnings surface in styles diagnostics', async ({ page }) => {

playwright/github-byot-ai.spec.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -760,15 +760,32 @@ test('BYOT remembers selected repository across reloads', async ({ page }) => {
760760
.fill('github_pat_fake_1234567890')
761761
await page.getByRole('button', { name: 'Add GitHub token' }).click()
762762

763-
await ensureOpenPrDrawerOpen(page)
764-
765763
const repoSelect = page.getByLabel('Pull request repository')
766-
await expect(repoSelect).toBeEnabled()
764+
await expect(repoSelect).toBeDisabled()
767765
await expect(page.getByRole('status', { name: 'App status' })).toHaveText(
768766
'Loaded 2 writable repositories',
769767
)
770768

771-
await repoSelect.selectOption('knightedcodemonkey/develop')
769+
await page.getByRole('button', { name: 'Workspaces' }).click()
770+
const workspaceRepositoryFilter = page.getByLabel('Workspace repository filter')
771+
const storedContextsSelect = page.getByLabel('Stored local editor contexts')
772+
const openStoredContextButton = page.getByRole('button', {
773+
name: 'Open',
774+
exact: true,
775+
})
776+
await expect(workspaceRepositoryFilter).toBeVisible()
777+
await workspaceRepositoryFilter.selectOption('knightedcodemonkey/develop')
778+
await expect(workspaceRepositoryFilter).toHaveValue('knightedcodemonkey/develop')
779+
780+
await expect(storedContextsSelect).toBeVisible()
781+
await storedContextsSelect.selectOption({
782+
label: 'Start new context for knightedcodemonkey/develop',
783+
})
784+
await expect(openStoredContextButton).toBeEnabled()
785+
await openStoredContextButton.click()
786+
await page.getByRole('button', { name: 'Close workspaces drawer' }).click()
787+
788+
await ensureOpenPrDrawerOpen(page)
772789
await expect(repoSelect).toHaveValue('knightedcodemonkey/develop')
773790

774791
await page.reload()
@@ -783,4 +800,5 @@ test('BYOT remembers selected repository across reloads', async ({ page }) => {
783800
await expect(page.getByRole('button', { name: 'Delete GitHub token' })).toBeVisible()
784801
await ensureOpenPrDrawerOpen(page)
785802
await expect(repoSelect).toHaveValue('knightedcodemonkey/develop')
803+
await expect(repoSelect).toBeDisabled()
786804
})

0 commit comments

Comments
 (0)