diff --git a/.github/workflows/aimock-drift.yml b/.github/workflows/aimock-drift.yml
index 3ab1e9226..c0f8e1791 100644
--- a/.github/workflows/aimock-drift.yml
+++ b/.github/workflows/aimock-drift.yml
@@ -20,7 +20,7 @@ jobs:
- name: Run drift check
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- run: npx nx run examples-chat-aimock-e2e:drift --skip-nx-cache
+ run: npx nx run examples-chat-angular:drift --skip-nx-cache
- name: Open issue on drift
if: failure()
uses: actions/github-script@v8
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1f1809d26..adad65d5f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -126,8 +126,8 @@ jobs:
run: uv sync
- run: npx nx run examples-chat-python:smoke --skip-nx-cache
- examples-chat-aimock-e2e:
- name: examples/chat — aimock e2e
+ examples-chat-e2e:
+ name: examples/chat — e2e
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
@@ -143,13 +143,13 @@ jobs:
- working-directory: examples/chat/python
run: uv sync
- run: npx playwright install --with-deps chromium
- - run: npx nx run examples-chat-aimock-e2e:test --skip-nx-cache
+ - run: npx nx e2e examples-chat-angular --skip-nx-cache
- name: Upload Playwright trace on failure
if: failure()
uses: actions/upload-artifact@v4
with:
- name: aimock-e2e-trace
- path: examples/chat/aimock-e2e/test-results/
+ name: examples-chat-e2e-trace
+ path: examples/chat/angular/e2e/test-results/
retention-days: 7
cockpit-e2e:
@@ -222,7 +222,7 @@ jobs:
cockpit-secret-integration,
cockpit-deploy-smoke,
examples-chat-smoke,
- examples-chat-aimock-e2e,
+ examples-chat-e2e,
cockpit-e2e,
website-e2e,
]
diff --git a/cockpit/chat/subagents/angular/e2e/c-subagents.spec.ts b/cockpit/chat/subagents/angular/e2e/c-subagents.spec.ts
index 6cc6375f5..8292e8ac6 100644
--- a/cockpit/chat/subagents/angular/e2e/c-subagents.spec.ts
+++ b/cockpit/chat/subagents/angular/e2e/c-subagents.spec.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { test, expect } from '@playwright/test';
-import { sendPromptAndWait } from '../../../../../libs/internal/aimock-harness/src';
+import { sendPromptAndWait } from '../../../../../libs/e2e-harness/src';
const PROMPT = 'Plan a trip from LAX to JFK';
diff --git a/cockpit/chat/subagents/angular/e2e/global-setup-impl.ts b/cockpit/chat/subagents/angular/e2e/global-setup-impl.ts
index ae254cd33..26f51ce9a 100644
--- a/cockpit/chat/subagents/angular/e2e/global-setup-impl.ts
+++ b/cockpit/chat/subagents/angular/e2e/global-setup-impl.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
-import { createGlobalSetup } from '../../../../../libs/internal/aimock-harness/src';
+import { createGlobalSetup } from '../../../../../libs/e2e-harness/src';
export default createGlobalSetup({
langgraphCwd: 'cockpit/langgraph/streaming/python',
diff --git a/cockpit/chat/subagents/angular/e2e/playwright.config.ts b/cockpit/chat/subagents/angular/e2e/playwright.config.ts
index ae04d7fb7..9900846a5 100644
--- a/cockpit/chat/subagents/angular/e2e/playwright.config.ts
+++ b/cockpit/chat/subagents/angular/e2e/playwright.config.ts
@@ -14,5 +14,5 @@ export default defineConfig({
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
globalSetup: './global-setup-impl.ts',
- globalTeardown: require.resolve('../../../../../libs/internal/aimock-harness/src/global-teardown'),
+ globalTeardown: require.resolve('../../../../../libs/e2e-harness/src/global-teardown'),
});
diff --git a/cockpit/chat/tool-calls/angular/e2e/c-tool-calls.spec.ts b/cockpit/chat/tool-calls/angular/e2e/c-tool-calls.spec.ts
index 2aa396c68..3a9c06aca 100644
--- a/cockpit/chat/tool-calls/angular/e2e/c-tool-calls.spec.ts
+++ b/cockpit/chat/tool-calls/angular/e2e/c-tool-calls.spec.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { test, expect } from '@playwright/test';
-import { sendPromptAndWait } from '../../../../../libs/internal/aimock-harness/src';
+import { sendPromptAndWait } from '../../../../../libs/e2e-harness/src';
const PROMPT = "What's the status of UA123?";
diff --git a/cockpit/chat/tool-calls/angular/e2e/global-setup-impl.ts b/cockpit/chat/tool-calls/angular/e2e/global-setup-impl.ts
index 9125b1a47..5c65ee875 100644
--- a/cockpit/chat/tool-calls/angular/e2e/global-setup-impl.ts
+++ b/cockpit/chat/tool-calls/angular/e2e/global-setup-impl.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
-import { createGlobalSetup } from '../../../../../libs/internal/aimock-harness/src';
+import { createGlobalSetup } from '../../../../../libs/e2e-harness/src';
export default createGlobalSetup({
langgraphCwd: 'cockpit/langgraph/streaming/python',
diff --git a/cockpit/chat/tool-calls/angular/e2e/playwright.config.ts b/cockpit/chat/tool-calls/angular/e2e/playwright.config.ts
index d473509d5..652db126c 100644
--- a/cockpit/chat/tool-calls/angular/e2e/playwright.config.ts
+++ b/cockpit/chat/tool-calls/angular/e2e/playwright.config.ts
@@ -14,5 +14,5 @@ export default defineConfig({
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
globalSetup: './global-setup-impl.ts',
- globalTeardown: require.resolve('../../../../../libs/internal/aimock-harness/src/global-teardown'),
+ globalTeardown: require.resolve('../../../../../libs/e2e-harness/src/global-teardown'),
});
diff --git a/cockpit/langgraph/streaming/angular/e2e/global-setup-impl.ts b/cockpit/langgraph/streaming/angular/e2e/global-setup-impl.ts
index 3f2390e7b..76fb474d0 100644
--- a/cockpit/langgraph/streaming/angular/e2e/global-setup-impl.ts
+++ b/cockpit/langgraph/streaming/angular/e2e/global-setup-impl.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { resolve } from 'node:path';
-import { createGlobalSetup } from '../../../../../libs/internal/aimock-harness/src';
+import { createGlobalSetup } from '../../../../../libs/e2e-harness/src';
export default createGlobalSetup({
langgraphCwd: 'cockpit/langgraph/streaming/python',
diff --git a/cockpit/langgraph/streaming/angular/e2e/playwright.config.ts b/cockpit/langgraph/streaming/angular/e2e/playwright.config.ts
index 9ec8c24f5..9592e1d79 100644
--- a/cockpit/langgraph/streaming/angular/e2e/playwright.config.ts
+++ b/cockpit/langgraph/streaming/angular/e2e/playwright.config.ts
@@ -14,5 +14,5 @@ export default defineConfig({
},
projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
globalSetup: './global-setup-impl.ts',
- globalTeardown: require.resolve('../../../../../libs/internal/aimock-harness/src/global-teardown'),
+ globalTeardown: require.resolve('../../../../../libs/e2e-harness/src/global-teardown'),
});
diff --git a/cockpit/langgraph/streaming/angular/e2e/streaming.spec.ts b/cockpit/langgraph/streaming/angular/e2e/streaming.spec.ts
index 18231181a..454ebe0cf 100644
--- a/cockpit/langgraph/streaming/angular/e2e/streaming.spec.ts
+++ b/cockpit/langgraph/streaming/angular/e2e/streaming.spec.ts
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT
import { test, expect } from '@playwright/test';
-import { sendPromptAndWait } from '../../../../../libs/internal/aimock-harness/src';
+import { sendPromptAndWait } from '../../../../../libs/e2e-harness/src';
test('streaming: assistant text from the mocked LLM renders in the cockpit chat composition', async ({ page }) => {
const bubble = await sendPromptAndWait(
diff --git a/docs/superpowers/plans/2026-05-16-e2e-harness-rename.md b/docs/superpowers/plans/2026-05-16-e2e-harness-rename.md
new file mode 100644
index 000000000..3f4932a47
--- /dev/null
+++ b/docs/superpowers/plans/2026-05-16-e2e-harness-rename.md
@@ -0,0 +1,593 @@
+# E2E harness rename + chat folder consistency implementation plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development. Steps use checkbox (`- [ ]`) syntax.
+
+**Goal:** Rename `libs/internal/aimock-harness/` → `libs/e2e-harness/`. Rename `examples/chat/aimock-e2e/` → `examples/chat/angular/e2e/`. Chat keeps its own inline harness (preserves working behavior); cockpit consumers update their relative imports to the new lib path.
+
+**Architecture:** Pure rename + path-relative updates. No semantic changes to either harness. Lib's `sendPromptAndWait` (cockpit-tuned) is already correct on main; no revert needed.
+
+**Tech Stack:** Nx, Playwright, `@copilotkit/aimock` (via the lib's runner).
+
+**Spec:** [docs/superpowers/specs/2026-05-16-e2e-harness-rename-design.md](../specs/2026-05-16-e2e-harness-rename-design.md)
+
+---
+
+## Working environment
+
+- Worktree: `/tmp/e2e-rename` (branch `claude/e2e-harness-rename`).
+- `node_modules` symlinked from main checkout; `npx`/`nx`/`uv` work directly.
+- License header `// SPDX-License-Identifier: MIT` on line 1 of any new TS file. None are CREATED here — all touches are moves + path/string edits.
+- One commit per task. DO NOT push, amend, or `git add -A`.
+- Spec commit (`d2400dff`) already on branch; this plan adds a second commit, then implementation.
+
+## Pre-flight survey (already verified by orchestrator, recorded here for reference)
+
+- Lib current state on main: `libs/internal/aimock-harness/src/{aimock-runner.ts, aimock-runner.spec.ts, global-setup-factory.ts, global-teardown.ts, index.ts, test-helpers.spec.ts, test-helpers.ts}` — 7 files.
+- Lib's `test-helpers.ts` already has the cockpit-tuned shape (Stop-generating waits + 5_000ms `toBeAttached`). NO revert needed.
+- Cockpit per-example dirs that reference the lib: 3
+ - `cockpit/langgraph/streaming/angular/e2e/`
+ - `cockpit/chat/tool-calls/angular/e2e/`
+ - `cockpit/chat/subagents/angular/e2e/`
+ - Each has 3 import lines using `../../../../../libs/internal/aimock-harness/src` (spec + global-setup-impl + playwright.config)
+- Chat dir current state: `examples/chat/aimock-e2e/{README.md, project.json, tsconfig.json, playwright.config.ts, global-setup.ts, global-teardown.ts, test-helpers.ts, aimock-runner.ts, aimock-runner.spec.ts, fixtures/, scripts/, 8 *.spec.ts}` — 8 specs total.
+
+## Path-segment notes
+
+- Cockpit per-example imports go to `repoRoot/libs/
/src` from `cockpit///angular/e2e/`. That's 5 segments up to root. Path becomes `'../../../../../libs/e2e-harness/src'`.
+- Chat's moved global-setup.ts goes from `examples/chat/aimock-e2e/` (3 segments to root) to `examples/chat/angular/e2e/` (4 segments to root). `REPO_ROOT = resolve(__dirname, '../../..')` becomes `'../../../..'`.
+
+---
+
+## Task 1: Rename `libs/internal/aimock-harness/` → `libs/e2e-harness/`
+
+**Files:**
+- Move (via `git mv`): entire `libs/internal/aimock-harness/` directory tree to `libs/e2e-harness/`
+- Modify: `libs/e2e-harness/project.json` — rename `name` field
+
+- [ ] **Step 1: Move the directory**
+
+```bash
+cd /tmp/e2e-rename
+mkdir -p libs
+git mv libs/internal/aimock-harness libs/e2e-harness
+```
+
+Verify:
+```bash
+ls libs/e2e-harness/src/
+```
+
+Expected output:
+```
+aimock-runner.spec.ts
+aimock-runner.ts
+global-setup-factory.ts
+global-teardown.ts
+index.ts
+test-helpers.spec.ts
+test-helpers.ts
+```
+
+If `libs/internal/` is now empty, remove it (Nx doesn't need empty intermediate dirs):
+
+```bash
+# Check first — there may be other libs under internal/
+ls libs/internal/ 2>/dev/null
+# If empty, remove
+rmdir libs/internal 2>/dev/null || true
+```
+
+- [ ] **Step 2: Update project.json name**
+
+Open `libs/e2e-harness/project.json`. Change the `name` field from `"internal-aimock-harness"` to `"e2e-harness"`. The `sourceRoot` should also be updated (if it references the old path).
+
+Before (likely):
+```json
+{
+ "name": "internal-aimock-harness",
+ "sourceRoot": "libs/internal/aimock-harness/src",
+ ...
+}
+```
+
+After:
+```json
+{
+ "name": "e2e-harness",
+ "sourceRoot": "libs/e2e-harness/src",
+ ...
+}
+```
+
+Verify JSON is still valid:
+```bash
+cd /tmp/e2e-rename
+python3 -c "import json; json.load(open('libs/e2e-harness/project.json'))" && echo "OK"
+```
+
+Expected: `OK`.
+
+- [ ] **Step 3: Update README references**
+
+Open `libs/e2e-harness/README.md`. Find references to `internal/aimock-harness`, `internal-aimock-harness`, `@ngaf-internal/aimock-harness` and update to `e2e-harness`, `@ngaf/e2e-harness` (if mentioned), etc. Edit conservatively — only rename references.
+
+If the README's "Per-example consumer shape" section references cockpit-only patterns, also add a one-line note that chat uses its own inline harness and consumes only `startAimock` from this lib (or doesn't consume anything; chat keeps its own runner copy for now).
+
+- [ ] **Step 4: Run lib vitest**
+
+```bash
+cd /tmp/e2e-rename/libs/e2e-harness
+npx vitest run
+```
+
+Expected: 5 passed (3 from aimock-runner + 2 from test-helpers).
+
+- [ ] **Step 5: Type-check the lib**
+
+```bash
+cd /tmp/e2e-rename/libs/e2e-harness
+npx tsc --noEmit
+```
+
+Expected: no errors.
+
+- [ ] **Step 6: Commit Task 1**
+
+```bash
+cd /tmp/e2e-rename
+git add libs/e2e-harness/project.json libs/e2e-harness/README.md
+git commit -m "chore(e2e-harness): rename libs/internal/aimock-harness → libs/e2e-harness"
+```
+
+(`git mv` from Step 1 staged the moves.)
+
+---
+
+## Task 2: Update cockpit per-example imports to new lib path
+
+**Files (3 dirs × 3 imports = 9 lines):**
+- `cockpit/langgraph/streaming/angular/e2e/{streaming.spec.ts, global-setup-impl.ts, playwright.config.ts}`
+- `cockpit/chat/tool-calls/angular/e2e/{c-tool-calls.spec.ts, global-setup-impl.ts, playwright.config.ts}`
+- `cockpit/chat/subagents/angular/e2e/{c-subagents.spec.ts, global-setup-impl.ts, playwright.config.ts}`
+
+- [ ] **Step 1: Bulk-replace the import path across all cockpit per-example files**
+
+```bash
+cd /tmp/e2e-rename
+find cockpit -path "*/angular/e2e/*.ts" -type f -exec sed -i '' \
+ -e 's|libs/internal/aimock-harness/src|libs/e2e-harness/src|g' \
+ {} +
+```
+
+- [ ] **Step 2: Verify all references updated**
+
+```bash
+cd /tmp/e2e-rename
+grep -rn "internal/aimock-harness" cockpit/ 2>/dev/null
+```
+
+Expected: zero matches.
+
+```bash
+grep -rn "libs/e2e-harness/src" cockpit/ 2>/dev/null | wc -l
+```
+
+Expected: at least 9 (3 per dir × 3 dirs).
+
+- [ ] **Step 3: Type-check one of the cockpit e2e dirs**
+
+```bash
+cd /tmp/e2e-rename/cockpit/chat/tool-calls/angular/e2e
+npx tsc --noEmit
+```
+
+Expected: no errors.
+
+- [ ] **Step 4: Commit Task 2**
+
+```bash
+cd /tmp/e2e-rename
+git add cockpit/langgraph/streaming/angular/e2e/ \
+ cockpit/chat/tool-calls/angular/e2e/ \
+ cockpit/chat/subagents/angular/e2e/
+git commit -m "chore(cockpit-e2e): update lib imports to libs/e2e-harness path"
+```
+
+---
+
+## Task 3: Move `examples/chat/aimock-e2e/` → `examples/chat/angular/e2e/`
+
+**Files:**
+- Move (via `git mv`): entire `examples/chat/aimock-e2e/` directory tree to `examples/chat/angular/e2e/`
+- Modify: `examples/chat/angular/e2e/global-setup.ts` — `REPO_ROOT` relative-path adjustment for new depth
+
+- [ ] **Step 1: Confirm `examples/chat/angular/e2e/` doesn't already exist**
+
+```bash
+cd /tmp/e2e-rename
+ls examples/chat/angular/e2e 2>/dev/null
+```
+
+Expected: error / "no such file". If a stray `e2e/` already exists under `examples/chat/angular/`, STOP and report.
+
+- [ ] **Step 2: Move the directory**
+
+```bash
+cd /tmp/e2e-rename
+git mv examples/chat/aimock-e2e examples/chat/angular/e2e
+```
+
+- [ ] **Step 3: Update `REPO_ROOT` in moved global-setup.ts**
+
+Open `examples/chat/angular/e2e/global-setup.ts`. Find:
+
+```typescript
+const REPO_ROOT = resolve(__dirname, '../../..');
+```
+
+Replace with (one additional `../`):
+
+```typescript
+const REPO_ROOT = resolve(__dirname, '../../../..');
+```
+
+- [ ] **Step 4: Audit other `__dirname`-relative paths in moved files**
+
+```bash
+cd /tmp/e2e-rename
+grep -rn "__dirname\|\\.\\./\\.\\./\\.\\." examples/chat/angular/e2e/ 2>/dev/null | grep -v node_modules
+```
+
+Inspect each match. For each, determine if it was depth-3-relative-to-old-location and needs to become depth-4-relative-to-new-location. Typical patterns to update:
+
+- `resolve(__dirname, '../../..')` → `resolve(__dirname, '../../../..')`
+- `path.join(__dirname, '../..', 'fixtures')` → no change (relative to its own dir, not repo root)
+
+The fixtures dir reference inside global-setup.ts is `resolve(__dirname, 'fixtures')` — relative to its own dir, no change needed.
+
+Inside `scripts/record.ts` and `scripts/drift.ts`, check for similar patterns. If found, update.
+
+- [ ] **Step 5: Verify the move**
+
+```bash
+cd /tmp/e2e-rename
+ls examples/chat/angular/e2e/
+```
+
+Expected: same file listing as `examples/chat/aimock-e2e/` had pre-move (8 spec files, fixtures/, scripts/, project.json, etc.).
+
+- [ ] **Step 6: Commit Task 3**
+
+```bash
+cd /tmp/e2e-rename
+git add examples/chat/angular/e2e/
+git commit -m "chore(examples-chat): move aimock-e2e → angular/e2e for folder consistency"
+```
+
+---
+
+## Task 4: Dissolve chat Nx project; add `e2e` target to `examples-chat-angular`
+
+**Files:**
+- Delete: `examples/chat/angular/e2e/project.json`
+- Modify: `examples/chat/angular/project.json` — add `e2e` target (and optionally `record`, `drift`)
+
+- [ ] **Step 1: Inspect the chat e2e project.json**
+
+```bash
+cd /tmp/e2e-rename
+cat examples/chat/angular/e2e/project.json
+```
+
+Note the targets present (`test`, `record`, `drift`). The `test` target becomes `e2e` on the angular project. `record` + `drift` are dev-only; either move to the angular project or drop entirely (the scripts can be shell-invoked directly).
+
+- [ ] **Step 2: Delete the chat e2e project.json**
+
+```bash
+cd /tmp/e2e-rename
+git rm examples/chat/angular/e2e/project.json
+```
+
+- [ ] **Step 3: Add `e2e`, `record`, `drift` targets to examples-chat-angular project.json**
+
+Open `examples/chat/angular/project.json`. Add to `targets`:
+
+```json
+"e2e": {
+ "executor": "@nx/playwright:playwright",
+ "options": {
+ "config": "examples/chat/angular/e2e/playwright.config.ts"
+ }
+},
+"record": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "examples/chat/angular/e2e",
+ "command": "tsx scripts/record.ts"
+ }
+},
+"drift": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "examples/chat/angular/e2e",
+ "command": "tsx scripts/drift.ts"
+ }
+}
+```
+
+Position them near the existing targets. Maintain JSON syntax (commas).
+
+Verify:
+```bash
+cd /tmp/e2e-rename
+python3 -c "import json; d=json.load(open('examples/chat/angular/project.json')); print('targets:', list(d['targets'].keys()))"
+```
+
+Expected: targets list includes `e2e`, `record`, `drift` plus the existing ones.
+
+- [ ] **Step 4: Verify the now-orphaned `examples-chat-aimock-e2e` Nx project name no longer exists**
+
+```bash
+cd /tmp/e2e-rename
+npx nx show projects 2>&1 | grep -E "examples-chat-aimock-e2e|examples-chat-angular" | head
+```
+
+Expected: no `examples-chat-aimock-e2e` (we deleted its project.json). `examples-chat-angular` present.
+
+If Nx complains about the path, run `npx nx reset` to clear the daemon cache.
+
+- [ ] **Step 5: Commit Task 4**
+
+```bash
+cd /tmp/e2e-rename
+git add examples/chat/angular/project.json
+git commit -m "chore(examples-chat): dissolve aimock-e2e Nx project; add e2e/record/drift targets to angular project"
+```
+
+---
+
+## Task 5: Update CI workflows
+
+**Files:**
+- Modify: `.github/workflows/ci.yml`
+- Modify: `.github/workflows/aimock-drift.yml`
+
+- [ ] **Step 1: Update ci.yml chat job**
+
+Open `.github/workflows/ci.yml`. Find the `examples-chat-aimock-e2e` job. Replace its definition:
+
+OLD (find this block):
+```yaml
+ examples-chat-aimock-e2e:
+ name: examples/chat — aimock e2e
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6.0.2
+ - uses: actions/setup-node@v6.3.0
+ with:
+ node-version: 22
+ cache: npm
+ - name: Install uv
+ uses: astral-sh/setup-uv@v8.0.0
+ with:
+ python-version: '3.12'
+ - run: npm ci
+ - working-directory: examples/chat/python
+ run: uv sync
+ - run: npx playwright install --with-deps chromium
+ - run: npx nx run examples-chat-aimock-e2e:test --skip-nx-cache
+ - name: Upload Playwright trace on failure
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: aimock-e2e-trace
+ path: examples/chat/aimock-e2e/test-results/
+ retention-days: 7
+```
+
+NEW (rename id, display name, command, artifact name + path):
+```yaml
+ examples-chat-e2e:
+ name: examples/chat — e2e
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6.0.2
+ - uses: actions/setup-node@v6.3.0
+ with:
+ node-version: 22
+ cache: npm
+ - name: Install uv
+ uses: astral-sh/setup-uv@v8.0.0
+ with:
+ python-version: '3.12'
+ - run: npm ci
+ - working-directory: examples/chat/python
+ run: uv sync
+ - run: npx playwright install --with-deps chromium
+ - run: npx nx e2e examples-chat-angular --skip-nx-cache
+ - name: Upload Playwright trace on failure
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: examples-chat-e2e-trace
+ path: examples/chat/angular/e2e/test-results/
+ retention-days: 7
+```
+
+- [ ] **Step 2: Update `deploy.needs` array in ci.yml**
+
+Find the `deploy` job's `needs:` array. Replace `examples-chat-aimock-e2e` with `examples-chat-e2e`.
+
+- [ ] **Step 3: Update aimock-drift.yml**
+
+Open `.github/workflows/aimock-drift.yml`. Find:
+
+```yaml
+ run: npx nx run examples-chat-aimock-e2e:drift --skip-nx-cache
+```
+
+Replace with:
+
+```yaml
+ run: npx nx run examples-chat-angular:drift --skip-nx-cache
+```
+
+(Or the equivalent direct shell invocation `npx tsx examples/chat/angular/e2e/scripts/drift.ts` — both work; nx target is more consistent with the new layout.)
+
+- [ ] **Step 4: Verify both workflows parse**
+
+```bash
+cd /tmp/e2e-rename
+npx -y js-yaml .github/workflows/ci.yml > /dev/null && echo "ci.yml OK"
+npx -y js-yaml .github/workflows/aimock-drift.yml > /dev/null && echo "aimock-drift.yml OK"
+```
+
+Expected: both `OK`.
+
+- [ ] **Step 5: Commit Task 5**
+
+```bash
+cd /tmp/e2e-rename
+git add .github/workflows/ci.yml .github/workflows/aimock-drift.yml
+git commit -m "ci(examples-chat): rename job and target to new e2e layout"
+```
+
+---
+
+## Task 6: Final reference grep
+
+**Files:** none — verification only.
+
+- [ ] **Step 1: Final grep for stale references**
+
+```bash
+cd /tmp/e2e-rename
+grep -rn "internal/aimock-harness\|examples-chat-aimock-e2e\|examples/chat/aimock-e2e" \
+ --include='*.ts' --include='*.json' --include='*.yml' --include='*.md' \
+ . | grep -v "node_modules\|test-results\|playwright-report\|docs/superpowers/"
+```
+
+Expected: zero matches (the spec/plan docs under `docs/superpowers/` are excluded since they document the rename).
+
+If any non-docs matches remain, fix them in this task before considering complete.
+
+- [ ] **Step 2: Final grep for the OLD lib name**
+
+```bash
+cd /tmp/e2e-rename
+grep -rn "internal-aimock-harness\|@ngaf-internal/aimock-harness" \
+ --include='*.ts' --include='*.json' --include='*.yml' --include='*.md' \
+ . | grep -v "node_modules\|docs/superpowers/"
+```
+
+Expected: zero matches.
+
+If matches remain, edit them (likely tsconfig path aliases — if there's a `paths` entry, update to `e2e-harness`).
+
+- [ ] **Step 3: Commit Task 6 ONLY IF edits happened**
+
+If Steps 1 or 2 found stale references that you edited, commit:
+
+```bash
+cd /tmp/e2e-rename
+git add -p # interactively stage just the cleanups
+git commit -m "chore: clean up stale internal-aimock-harness references"
+```
+
+If both grep steps found zero matches, no commit needed; skip to Task 7.
+
+---
+
+## Task 7: Verify locally
+
+- [ ] **Step 1: Set up env**
+
+```bash
+cd /tmp/e2e-rename
+cp /Users/blove/repos/angular-agent-framework/examples/chat/python/.env examples/chat/python/.env
+node libs/licensing/scripts/generate-public-key.mjs 2>&1 | tail -1
+npx playwright install --with-deps chromium # idempotent
+```
+
+- [ ] **Step 2: Run lib vitest**
+
+```bash
+cd /tmp/e2e-rename/libs/e2e-harness
+npx vitest run
+```
+
+Expected: 5 passed.
+
+- [ ] **Step 3: Run chat e2e**
+
+```bash
+cd /tmp/e2e-rename
+lsof -ti :2024 :4200 2>/dev/null | xargs kill -9 2>/dev/null
+ps aux | grep -E "uv |langgraph dev" | grep -v grep | awk '{print $2}' | xargs kill -9 2>/dev/null
+sleep 5
+npx nx e2e examples-chat-angular --skip-nx-cache 2>&1 | tail -8
+```
+
+Expected: all 8 chat specs pass (or however many were passing pre-rename — chat's behavior is preserved exactly). Wall-clock ~2-3 min.
+
+If anything fails, STOP and report.
+
+- [ ] **Step 4: Run each cockpit e2e**
+
+```bash
+cd /tmp/e2e-rename
+for proj in cockpit-langgraph-streaming-angular cockpit-chat-tool-calls-angular cockpit-chat-subagents-angular; do
+ echo "=== $proj ==="
+ lsof -ti :8123 :8124 :8125 :4300 :4504 :4505 2>/dev/null | xargs kill -9 2>/dev/null
+ ps aux | grep -E "uv |langgraph dev" | grep -v grep | awk '{print $2}' | xargs kill -9 2>/dev/null
+ sleep 5
+ npx nx e2e "$proj" --skip-nx-cache 2>&1 | tail -3
+done
+```
+
+Expected: all 3 cockpit projects pass. Wall-clock ~5-8 min total.
+
+If anything fails, STOP and report.
+
+- [ ] **Step 5: Confirm working tree is clean**
+
+```bash
+cd /tmp/e2e-rename
+rm -rf examples/chat/angular/e2e/test-results examples/chat/angular/e2e/playwright-report
+rm -rf cockpit/*/angular/e2e/test-results cockpit/*/angular/e2e/playwright-report
+git status --short
+```
+
+Expected: only `node_modules` symlink as untracked.
+
+---
+
+## Task 8: Push, open PR, watch CI, merge
+
+(Orchestrator handles — implementer STOPS after Task 7.)
+
+- Push branch.
+- Open PR titled `chore: rename aimock-harness → e2e-harness; chat aimock-e2e → angular/e2e for folder consistency`.
+- Watch CI: `examples/chat — e2e` and `Cockpit — e2e` must both pass green.
+- Merge with `--squash` when green; clean up worktree.
+
+---
+
+## Self-review checklist
+
+- [x] Spec coverage:
+ - Lib rename + project.json + README → Task 1
+ - Cockpit consumer import updates → Task 2
+ - Chat dir move + REPO_ROOT adjustment → Task 3
+ - Dissolve chat Nx project + add targets to angular project → Task 4
+ - CI updates → Task 5
+ - Final grep cleanup → Task 6
+ - Local verification → Task 7
+ - Push + PR + merge → Task 8 (orchestrator)
+- [x] Placeholder scan: no TBD/TODO. Conditional commit in Task 6 ("only if edits happened") is intentional — the find-and-fix loop is real work, but might find nothing if the rename was complete.
+- [x] Type consistency: `e2e-harness` is the only new identifier introduced. Used consistently.
+- [x] No new code authored — all tasks are file moves + string-replace edits + JSON modifications.
+
+## Execution handoff
+
+Plan complete. Recommended: **subagent-driven-development**, one implementer for Tasks 1-7 (sequential, mechanical). Orchestrator handles Task 8 (push + PR + merge).
diff --git a/docs/superpowers/specs/2026-05-16-e2e-harness-rename-design.md b/docs/superpowers/specs/2026-05-16-e2e-harness-rename-design.md
new file mode 100644
index 000000000..726dae75e
--- /dev/null
+++ b/docs/superpowers/specs/2026-05-16-e2e-harness-rename-design.md
@@ -0,0 +1,175 @@
+# E2E harness rename + chat folder consistency — design
+
+> **Place in the larger plan.** Reframe after the aborted full migration ([discussion in this branch's history]; not pushed). Lessons learned:
+> 1. The chat and cockpit harnesses solve different problems — chat is single-turn fast replays of one rich app; cockpit is per-app multi-turn tool/subagent flows. The lib's `sendPromptAndWait` is correctly tuned for cockpit but doesn't fit chat.
+> 2. The only truly shared piece is the aimock runner (`startAimock` + `AimockHandle`). Process orchestration glue is shared between cockpit examples but NOT with chat.
+> 3. The earlier "one helper for everyone" framing made both harnesses worse. Partial sharing (runner only) is the right shape.
+
+## Goal
+
+Rename the harness library from `libs/internal/aimock-harness/` to `libs/e2e-harness/`. Rename the chat aimock e2e dir from `examples/chat/aimock-e2e/` to `examples/chat/angular/e2e/` (location-only, behavior preserved). Chat continues to use its own inline harness modules; the lib's broader API (`createGlobalSetup`, `sendPromptAndWait`, `global-teardown`) stays for cockpit's exclusive use. Chat consumes ONLY `startAimock` from the lib (or stays fully self-contained by keeping its own runner — TBD per-implementation, both are fine).
+
+No behavior change for either harness. All 10 chat specs + all 5+ cockpit specs continue to pass.
+
+## Non-goals
+
+- Changing chat's harness semantics (helpers, wait shape, teardown) — all preserved exactly.
+- Changing cockpit harness semantics — preserved exactly.
+- Sharing the lib's `sendPromptAndWait` or `createGlobalSetup` with chat. Chat keeps its own.
+- Adding new e2e coverage anywhere.
+- Renaming Angular projects (`examples-chat-angular`, `cockpit-*-angular` all unchanged).
+- Renaming Python projects.
+
+## What changes
+
+### Lib rename: `libs/internal/aimock-harness/` → `libs/e2e-harness/`
+
+- `git mv` the entire directory.
+- `project.json`: name `internal-aimock-harness` → `e2e-harness`. `sourceRoot` updates.
+- README updates: new project name; clarifies that the lib's broader API is cockpit-tuned and chat uses only the runner.
+- All cockpit per-example dirs update their relative imports:
+ - OLD: `'../../../../../libs/internal/aimock-harness/src'` (and `/global-teardown`)
+ - NEW: `'../../../../../libs/e2e-harness/src'` (and `/global-teardown`)
+ - Same 5-segment depth to repo root; only the post-root path simplifies.
+
+### Lib's `sendPromptAndWait` reverted to working cockpit-tuned shape
+
+During the failed migration I changed the lib helper trying to make it chat-friendly. Revert to the cockpit-tuned version that passed c-tool-calls + c-subagents (the agent-idle / "Stop generating" wait):
+
+```typescript
+// Wait for the agent to enter the loading state (Stop generating visible).
+await expect(page.getByRole('button', { name: /stop generating/i })).toBeVisible({ timeout: 10_000 });
+// Wait for the agent to fully finish.
+await expect(page.getByRole('button', { name: /stop generating/i })).not.toBeAttached({ timeout: 60_000 });
+// Return the last finalized assistant bubble.
+const finalizedAssistant = page.locator('chat-message[data-role="assistant"][data-streaming="false"]').last();
+await expect(finalizedAssistant).toBeAttached({ timeout: 5_000 });
+return finalizedAssistant;
+```
+
+This was working pre-session. The earlier modifications hurt chat without helping cockpit; revert.
+
+### Chat dir rename: `examples/chat/aimock-e2e/` → `examples/chat/angular/e2e/`
+
+Pure file move + path adjustments. All chat harness files (`aimock-runner.ts`, `test-helpers.ts`, `global-setup.ts`, `global-teardown.ts`, `playwright.config.ts`, fixtures, scripts, specs, README, project.json, tsconfig.json) move verbatim. Internal relative paths inside the moved files may need adjustment (e.g., `REPO_ROOT = resolve(__dirname, '../../..')` in `global-setup.ts` becomes `'../../../..'` due to one more directory level).
+
+The chat harness keeps its own `aimock-runner.ts` for now. Option to thin it to a re-export from `@e2e-harness` later, but not in this PR — the runner copy is harmless and decouples chat from any future lib changes.
+
+### Chat Nx project dissolved; `e2e` target rolls into `examples-chat-angular`
+
+- Delete `examples/chat/aimock-e2e/project.json` (now `examples/chat/angular/e2e/project.json` — delete).
+- Add to `examples/chat/angular/project.json`:
+ ```json
+ "e2e": {
+ "executor": "@nx/playwright:playwright",
+ "options": { "config": "examples/chat/angular/e2e/playwright.config.ts" }
+ }
+ ```
+- The `record` and `drift` targets that lived in the chat aimock project.json — move to `examples-chat-angular`'s project.json too (or drop; they're dev-only and the scripts can be shell-invoked directly).
+
+### CI updates
+
+- `ci.yml`:
+ - Job id `examples-chat-aimock-e2e` → `examples-chat-e2e`
+ - Display name `examples/chat — aimock e2e` → `examples/chat — e2e`
+ - Command `npx nx run examples-chat-aimock-e2e:test --skip-nx-cache` → `npx nx e2e examples-chat-angular --skip-nx-cache`
+ - Trace artifact path → `examples/chat/angular/e2e/test-results/`, name → `examples-chat-e2e-trace`
+ - `deploy.needs` array: swap `examples-chat-aimock-e2e` → `examples-chat-e2e`
+- `aimock-drift.yml`:
+ - `npx nx run examples-chat-aimock-e2e:drift --skip-nx-cache` → `npx tsx examples/chat/angular/e2e/scripts/drift.ts`
+
+## File deltas at a glance
+
+### Renames (git mv)
+
+- `libs/internal/aimock-harness/` → `libs/e2e-harness/` (whole dir + contents)
+- `examples/chat/aimock-e2e/` → `examples/chat/angular/e2e/` (whole dir + contents)
+
+### Modifications
+
+- `libs/e2e-harness/project.json` — `name` field
+- `libs/e2e-harness/README.md` — rename references + scope clarification
+- `libs/e2e-harness/src/test-helpers.ts` — revert to working cockpit-tuned shape
+- Per-example cockpit dirs (3 currently): playwright.config.ts + global-setup-impl.ts + any spec files — update relative imports from `libs/internal/aimock-harness/src` to `libs/e2e-harness/src`
+- `examples/chat/angular/e2e/global-setup.ts` — `REPO_ROOT` relative path adjustment for new depth
+- `examples/chat/angular/project.json` — add `e2e` target (and optional `record`, `drift` targets)
+- `.github/workflows/ci.yml` — chat job rename + path/command updates + `deploy.needs` update
+- `.github/workflows/aimock-drift.yml` — invocation path update
+
+### Deletions
+
+- `examples/chat/angular/e2e/project.json` (the `examples-chat-aimock-e2e` Nx project dissolves)
+- The empty `examples/chat/aimock-e2e/` directory (after move)
+
+## Components
+
+### Cockpit per-example imports
+
+Each of `cockpit///angular/e2e/global-setup-impl.ts` has:
+
+```typescript
+import { createGlobalSetup } from '../../../../../libs/internal/aimock-harness/src';
+```
+
+Becomes:
+
+```typescript
+import { createGlobalSetup } from '../../../../../libs/e2e-harness/src';
+```
+
+Each `playwright.config.ts` has:
+
+```typescript
+globalTeardown: require.resolve('../../../../../libs/internal/aimock-harness/src/global-teardown'),
+```
+
+Becomes:
+
+```typescript
+globalTeardown: require.resolve('../../../../../libs/e2e-harness/src/global-teardown'),
+```
+
+Cockpit per-example dirs touched (verify during implementation):
+- `cockpit/langgraph/streaming/angular/e2e/`
+- `cockpit/chat/tool-calls/angular/e2e/`
+- `cockpit/chat/subagents/angular/e2e/`
+
+Their spec files generally don't import from the lib (they import from `sendPromptAndWait` via the global-setup-impl path or via a wrapper). Verify with grep.
+
+### Chat e2e dir internal references
+
+After the move, `examples/chat/angular/e2e/global-setup.ts` has:
+
+```typescript
+const REPO_ROOT = resolve(__dirname, '../../..');
+```
+
+The old `examples/chat/aimock-e2e/` was 3 levels deep (`examples/chat/aimock-e2e/`). The new `examples/chat/angular/e2e/` is 4 levels deep. So:
+
+```typescript
+const REPO_ROOT = resolve(__dirname, '../../../..');
+```
+
+Other `__dirname`-relative paths in the chat global-setup, scripts, etc. — audit and update.
+
+## Risk surface
+
+- **Chat helper behavior** must NOT change. The migration's failure mode was a lib helper modification breaking chat markdown rendering. This rename keeps chat's own helper intact, so the failure mode can't repeat.
+- **Cockpit helper behavior** must NOT change. Reverting the lib's `sendPromptAndWait` to the pre-session working version restores the cockpit-tuned signal.
+- **Relative imports** for cockpit per-example dirs need correct path-segment count. Easy to typo; grep verification after the move.
+- **`REPO_ROOT` resolution** inside chat's moved global-setup needs the right number of `..` segments. Audit script needed.
+
+## Acceptance criteria
+
+- `libs/internal/aimock-harness/` directory is gone; `libs/e2e-harness/` exists with all the same files (renamed via `git mv`).
+- `libs/e2e-harness/project.json` has `name: "e2e-harness"`.
+- `libs/e2e-harness/src/test-helpers.ts` matches the pre-session cockpit-tuned shape.
+- `examples/chat/aimock-e2e/` directory is gone; `examples/chat/angular/e2e/` exists with all the same files.
+- `examples/chat/angular/project.json` has an `e2e` target.
+- `examples-chat-aimock-e2e` Nx project no longer exists.
+- `nx e2e examples-chat-angular` passes locally with 3/3 consecutive stability runs. All 7 chat specs + the runner spec pass.
+- `nx e2e cockpit-langgraph-streaming-angular`, `nx e2e cockpit-chat-tool-calls-angular`, `nx e2e cockpit-chat-subagents-angular` all pass — proves the cockpit lib import rename worked.
+- `nx run-many --target=e2e --projects=cockpit-*-angular` (or the equivalent CI loop) passes.
+- `ci.yml`'s `examples-chat-e2e` job is green; `deploy.needs` updated.
+- `aimock-drift.yml` YAML parses (manual `workflow_dispatch` verification is optional post-merge).
+- Final reference grep `grep -rn 'internal/aimock-harness\|examples-chat-aimock-e2e\|examples/chat/aimock-e2e' ... ` returns zero matches outside `docs/superpowers/`.
diff --git a/examples/chat/aimock-e2e/project.json b/examples/chat/aimock-e2e/project.json
deleted file mode 100644
index 70467a2e7..000000000
--- a/examples/chat/aimock-e2e/project.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "name": "examples-chat-aimock-e2e",
- "$schema": "../../../node_modules/nx/schemas/project-schema.json",
- "projectType": "application",
- "sourceRoot": "examples/chat/aimock-e2e",
- "targets": {
- "test": {
- "executor": "nx:run-commands",
- "options": {
- "cwd": "examples/chat/aimock-e2e",
- "command": "playwright test"
- }
- },
- "record": {
- "executor": "nx:run-commands",
- "options": {
- "cwd": "examples/chat/aimock-e2e",
- "command": "tsx scripts/record.ts"
- }
- },
- "drift": {
- "executor": "nx:run-commands",
- "options": {
- "cwd": "examples/chat/aimock-e2e",
- "command": "tsx scripts/drift.ts"
- }
- }
- }
-}
diff --git a/examples/chat/aimock-e2e/.gitignore b/examples/chat/angular/e2e/.gitignore
similarity index 100%
rename from examples/chat/aimock-e2e/.gitignore
rename to examples/chat/angular/e2e/.gitignore
diff --git a/examples/chat/aimock-e2e/README.md b/examples/chat/angular/e2e/README.md
similarity index 87%
rename from examples/chat/aimock-e2e/README.md
rename to examples/chat/angular/e2e/README.md
index 6eef37090..de15b6330 100644
--- a/examples/chat/aimock-e2e/README.md
+++ b/examples/chat/angular/e2e/README.md
@@ -1,11 +1,11 @@
-# examples-chat-aimock-e2e
+# examples-chat-angular e2e
Cross-stack E2E harness for the chat example. Uses [`@copilotkit/aimock`](https://github.com/CopilotKit/aimock) as a deterministic mock for LLM API calls; the Python LangGraph dev server is launched with `OPENAI_BASE_URL` pointed at it; Playwright drives the Angular `/embed` route in real Chromium.
## Run the suite
```
-npx nx run examples-chat-aimock-e2e:test
+npx nx e2e examples-chat-angular
```
Replay-only. No `OPENAI_API_KEY` needed. Reads committed fixtures from `fixtures/`.
@@ -13,7 +13,7 @@ Replay-only. No `OPENAI_API_KEY` needed. Reads committed fixtures from `fixtures
## Refresh a fixture
```
-OPENAI_API_KEY=sk-... npx nx run examples-chat-aimock-e2e:record -- hi
+OPENAI_API_KEY=sk-... npx nx run examples-chat-angular:record -- hi
```
Captures a real OpenAI run for the named scenario, writes the result to `fixtures/.json`. Commit the updated fixture.
@@ -21,7 +21,7 @@ Captures a real OpenAI run for the named scenario, writes the result to `fixture
## Detect fixture drift
```
-OPENAI_API_KEY=sk-... npx nx run examples-chat-aimock-e2e:drift
+OPENAI_API_KEY=sk-... npx nx run examples-chat-angular:drift
```
Re-records each committed fixture against real OpenAI and reports byte-level diffs. CI runs this on a daily schedule and opens an issue when drift is detected; it does NOT auto-update fixtures.
diff --git a/examples/chat/aimock-e2e/a2ui-single-bubble.spec.ts b/examples/chat/angular/e2e/a2ui-single-bubble.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/a2ui-single-bubble.spec.ts
rename to examples/chat/angular/e2e/a2ui-single-bubble.spec.ts
diff --git a/examples/chat/aimock-e2e/aimock-runner.spec.ts b/examples/chat/angular/e2e/aimock-runner.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/aimock-runner.spec.ts
rename to examples/chat/angular/e2e/aimock-runner.spec.ts
diff --git a/examples/chat/aimock-e2e/aimock-runner.ts b/examples/chat/angular/e2e/aimock-runner.ts
similarity index 100%
rename from examples/chat/aimock-e2e/aimock-runner.ts
rename to examples/chat/angular/e2e/aimock-runner.ts
diff --git a/examples/chat/aimock-e2e/fixtures/a2ui-surface.json b/examples/chat/angular/e2e/fixtures/a2ui-surface.json
similarity index 100%
rename from examples/chat/aimock-e2e/fixtures/a2ui-surface.json
rename to examples/chat/angular/e2e/fixtures/a2ui-surface.json
diff --git a/examples/chat/aimock-e2e/fixtures/hi.json b/examples/chat/angular/e2e/fixtures/hi.json
similarity index 100%
rename from examples/chat/aimock-e2e/fixtures/hi.json
rename to examples/chat/angular/e2e/fixtures/hi.json
diff --git a/examples/chat/aimock-e2e/fixtures/interrupt-approval.json b/examples/chat/angular/e2e/fixtures/interrupt-approval.json
similarity index 100%
rename from examples/chat/aimock-e2e/fixtures/interrupt-approval.json
rename to examples/chat/angular/e2e/fixtures/interrupt-approval.json
diff --git a/examples/chat/aimock-e2e/fixtures/markdown.json b/examples/chat/angular/e2e/fixtures/markdown.json
similarity index 100%
rename from examples/chat/aimock-e2e/fixtures/markdown.json
rename to examples/chat/angular/e2e/fixtures/markdown.json
diff --git a/examples/chat/aimock-e2e/fixtures/research-subagent.json b/examples/chat/angular/e2e/fixtures/research-subagent.json
similarity index 100%
rename from examples/chat/aimock-e2e/fixtures/research-subagent.json
rename to examples/chat/angular/e2e/fixtures/research-subagent.json
diff --git a/examples/chat/aimock-e2e/global-setup.ts b/examples/chat/angular/e2e/global-setup.ts
similarity index 97%
rename from examples/chat/aimock-e2e/global-setup.ts
rename to examples/chat/angular/e2e/global-setup.ts
index 32a55a398..eb9e42531 100644
--- a/examples/chat/aimock-e2e/global-setup.ts
+++ b/examples/chat/angular/e2e/global-setup.ts
@@ -15,7 +15,7 @@ declare global {
var __AIMOCK_E2E_STATE__: SharedState | undefined;
}
-const REPO_ROOT = resolve(__dirname, '../../..');
+const REPO_ROOT = resolve(__dirname, '../../../..');
const FIXTURE_PATH = process.env.AIMOCK_FIXTURE
? resolve(__dirname, process.env.AIMOCK_FIXTURE)
: resolve(__dirname, 'fixtures');
diff --git a/examples/chat/aimock-e2e/global-teardown.ts b/examples/chat/angular/e2e/global-teardown.ts
similarity index 100%
rename from examples/chat/aimock-e2e/global-teardown.ts
rename to examples/chat/angular/e2e/global-teardown.ts
diff --git a/examples/chat/aimock-e2e/interrupt-approval.spec.ts b/examples/chat/angular/e2e/interrupt-approval.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/interrupt-approval.spec.ts
rename to examples/chat/angular/e2e/interrupt-approval.spec.ts
diff --git a/examples/chat/aimock-e2e/markdown.spec.ts b/examples/chat/angular/e2e/markdown.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/markdown.spec.ts
rename to examples/chat/angular/e2e/markdown.spec.ts
diff --git a/examples/chat/aimock-e2e/playwright.config.ts b/examples/chat/angular/e2e/playwright.config.ts
similarity index 100%
rename from examples/chat/aimock-e2e/playwright.config.ts
rename to examples/chat/angular/e2e/playwright.config.ts
diff --git a/examples/chat/aimock-e2e/regenerate.spec.ts b/examples/chat/angular/e2e/regenerate.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/regenerate.spec.ts
rename to examples/chat/angular/e2e/regenerate.spec.ts
diff --git a/examples/chat/aimock-e2e/research-subagent.spec.ts b/examples/chat/angular/e2e/research-subagent.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/research-subagent.spec.ts
rename to examples/chat/angular/e2e/research-subagent.spec.ts
diff --git a/examples/chat/aimock-e2e/scripts/drift.ts b/examples/chat/angular/e2e/scripts/drift.ts
similarity index 100%
rename from examples/chat/aimock-e2e/scripts/drift.ts
rename to examples/chat/angular/e2e/scripts/drift.ts
diff --git a/examples/chat/aimock-e2e/scripts/record.ts b/examples/chat/angular/e2e/scripts/record.ts
similarity index 100%
rename from examples/chat/aimock-e2e/scripts/record.ts
rename to examples/chat/angular/e2e/scripts/record.ts
diff --git a/examples/chat/aimock-e2e/sidebar-mode-coexistence.spec.ts b/examples/chat/angular/e2e/sidebar-mode-coexistence.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/sidebar-mode-coexistence.spec.ts
rename to examples/chat/angular/e2e/sidebar-mode-coexistence.spec.ts
diff --git a/examples/chat/aimock-e2e/smoke.spec.ts b/examples/chat/angular/e2e/smoke.spec.ts
similarity index 100%
rename from examples/chat/aimock-e2e/smoke.spec.ts
rename to examples/chat/angular/e2e/smoke.spec.ts
diff --git a/examples/chat/aimock-e2e/test-helpers.ts b/examples/chat/angular/e2e/test-helpers.ts
similarity index 100%
rename from examples/chat/aimock-e2e/test-helpers.ts
rename to examples/chat/angular/e2e/test-helpers.ts
diff --git a/examples/chat/aimock-e2e/tsconfig.json b/examples/chat/angular/e2e/tsconfig.json
similarity index 100%
rename from examples/chat/aimock-e2e/tsconfig.json
rename to examples/chat/angular/e2e/tsconfig.json
diff --git a/examples/chat/angular/project.json b/examples/chat/angular/project.json
index 6f503ad09..06814c630 100644
--- a/examples/chat/angular/project.json
+++ b/examples/chat/angular/project.json
@@ -63,6 +63,26 @@
},
"lint": {
"executor": "@nx/eslint:lint"
+ },
+ "e2e": {
+ "executor": "@nx/playwright:playwright",
+ "options": {
+ "config": "examples/chat/angular/e2e/playwright.config.ts"
+ }
+ },
+ "record": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "examples/chat/angular/e2e",
+ "command": "tsx scripts/record.ts"
+ }
+ },
+ "drift": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "examples/chat/angular/e2e",
+ "command": "tsx scripts/drift.ts"
+ }
}
},
"tags": ["scope:examples", "type:app"]
diff --git a/libs/internal/aimock-harness/README.md b/libs/e2e-harness/README.md
similarity index 64%
rename from libs/internal/aimock-harness/README.md
rename to libs/e2e-harness/README.md
index b10391a94..efdde3594 100644
--- a/libs/internal/aimock-harness/README.md
+++ b/libs/e2e-harness/README.md
@@ -1,13 +1,16 @@
-# @ngaf-internal/aimock-harness
+# e2e-harness
Internal-only library that wraps [`@copilotkit/aimock`](https://github.com/CopilotKit/aimock) for our cockpit example aimock e2e suite.
-NOT published. The `@ngaf-internal/*` namespace is reserved for internal libraries that are tightly coupled to repo-specific orchestration (langgraph + Angular dev server boot) and shouldn't appear in consumer-facing API surfaces.
+NOT published. This lib is tightly coupled to repo-specific orchestration (langgraph + Angular dev server boot) and shouldn't appear in consumer-facing API surfaces.
+
+> The `examples/chat/angular/e2e/` suite currently maintains its own inline harness copy and does not consume this lib. Cockpit per-example e2e suites are the only consumers.
## API
```typescript
-import { createGlobalSetup, sendPromptAndWait } from '@ngaf-internal/aimock-harness';
+// Cockpit consumers import via repo-root-relative path (no published package):
+import { createGlobalSetup, sendPromptAndWait } from '../../../../../libs/e2e-harness/src';
```
- `createGlobalSetup(opts)` — returns a Playwright globalSetup function that boots aimock + langgraph + the named Angular dev server.
diff --git a/libs/internal/aimock-harness/project.json b/libs/e2e-harness/project.json
similarity index 55%
rename from libs/internal/aimock-harness/project.json
rename to libs/e2e-harness/project.json
index 74d394186..5d4e72ce6 100644
--- a/libs/internal/aimock-harness/project.json
+++ b/libs/e2e-harness/project.json
@@ -1,21 +1,21 @@
{
- "name": "internal-aimock-harness",
- "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "name": "e2e-harness",
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
- "sourceRoot": "libs/internal/aimock-harness/src",
+ "sourceRoot": "libs/e2e-harness/src",
"tags": ["scope:internal"],
"targets": {
"lint": {
"executor": "nx:run-commands",
"options": {
- "cwd": "libs/internal/aimock-harness",
+ "cwd": "libs/e2e-harness",
"command": "tsc --noEmit"
}
},
"test": {
"executor": "nx:run-commands",
"options": {
- "cwd": "libs/internal/aimock-harness",
+ "cwd": "libs/e2e-harness",
"command": "vitest run"
}
}
diff --git a/libs/internal/aimock-harness/src/aimock-runner.spec.ts b/libs/e2e-harness/src/aimock-runner.spec.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/aimock-runner.spec.ts
rename to libs/e2e-harness/src/aimock-runner.spec.ts
diff --git a/libs/internal/aimock-harness/src/aimock-runner.ts b/libs/e2e-harness/src/aimock-runner.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/aimock-runner.ts
rename to libs/e2e-harness/src/aimock-runner.ts
diff --git a/libs/internal/aimock-harness/src/global-setup-factory.ts b/libs/e2e-harness/src/global-setup-factory.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/global-setup-factory.ts
rename to libs/e2e-harness/src/global-setup-factory.ts
diff --git a/libs/internal/aimock-harness/src/global-teardown.ts b/libs/e2e-harness/src/global-teardown.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/global-teardown.ts
rename to libs/e2e-harness/src/global-teardown.ts
diff --git a/libs/internal/aimock-harness/src/index.ts b/libs/e2e-harness/src/index.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/index.ts
rename to libs/e2e-harness/src/index.ts
diff --git a/libs/internal/aimock-harness/src/test-helpers.spec.ts b/libs/e2e-harness/src/test-helpers.spec.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/test-helpers.spec.ts
rename to libs/e2e-harness/src/test-helpers.spec.ts
diff --git a/libs/internal/aimock-harness/src/test-helpers.ts b/libs/e2e-harness/src/test-helpers.ts
similarity index 100%
rename from libs/internal/aimock-harness/src/test-helpers.ts
rename to libs/e2e-harness/src/test-helpers.ts
diff --git a/libs/internal/aimock-harness/tsconfig.json b/libs/e2e-harness/tsconfig.json
similarity index 100%
rename from libs/internal/aimock-harness/tsconfig.json
rename to libs/e2e-harness/tsconfig.json
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 3436e1f35..453b76c99 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -39,8 +39,8 @@
"@ngaf/telemetry/browser": ["libs/telemetry/src/browser/public-api.ts"],
"@ngaf/telemetry/node": ["libs/telemetry/src/node/index.ts"],
"@ngaf/telemetry/shared": ["libs/telemetry/src/shared/public-api.ts"],
- "@ngaf-internal/aimock-harness": ["libs/internal/aimock-harness/src/index.ts"],
- "@ngaf-internal/aimock-harness/global-teardown": ["libs/internal/aimock-harness/src/global-teardown.ts"]
+ "@ngaf-internal/e2e-harness": ["libs/e2e-harness/src/index.ts"],
+ "@ngaf-internal/e2e-harness/global-teardown": ["libs/e2e-harness/src/global-teardown.ts"]
},
"skipLibCheck": true,
"strict": true,