code-coordinator 0.5.46__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_coordinator-0.5.46.dist-info/METADATA +625 -0
- code_coordinator-0.5.46.dist-info/RECORD +295 -0
- code_coordinator-0.5.46.dist-info/WHEEL +5 -0
- code_coordinator-0.5.46.dist-info/entry_points.txt +2 -0
- code_coordinator-0.5.46.dist-info/licenses/LICENSE +110 -0
- code_coordinator-0.5.46.dist-info/top_level.txt +1 -0
- coord/__init__.py +176 -0
- coord/_board_mapping.py +229 -0
- coord/acceptance.py +468 -0
- coord/acceptance_drivers.py +632 -0
- coord/agent.py +7517 -0
- coord/agent_app.py +1555 -0
- coord/agent_update.py +417 -0
- coord/agents/opencode/.gitignore +13 -0
- coord/agents/opencode/agents/work.md +129 -0
- coord/agents/opencode/routing.jsonc +49 -0
- coord/audit.py +301 -0
- coord/auto_loop.py +1440 -0
- coord/board_bool_guard.py +72 -0
- coord/board_service.py +141 -0
- coord/board_wire.py +309 -0
- coord/brain.py +581 -0
- coord/branch_model.py +214 -0
- coord/cargo_cache.py +258 -0
- coord/ci_github.py +386 -0
- coord/ci_store.py +560 -0
- coord/claim.py +353 -0
- coord/cli.py +454 -0
- coord/client.py +610 -0
- coord/commands/__init__.py +1 -0
- coord/commands/_common.py +329 -0
- coord/commands/acceptance.py +916 -0
- coord/commands/agent_ops.py +1339 -0
- coord/commands/audit.py +131 -0
- coord/commands/chat.py +320 -0
- coord/commands/dispatch.py +1780 -0
- coord/commands/dispatch_workers.py +4894 -0
- coord/commands/drive.py +616 -0
- coord/commands/drive_queue.py +1203 -0
- coord/commands/gate_a.py +217 -0
- coord/commands/gates.py +89 -0
- coord/commands/issues.py +681 -0
- coord/commands/lifecycle.py +513 -0
- coord/commands/merge.py +1900 -0
- coord/commands/milestone.py +2081 -0
- coord/commands/plan_followup.py +1243 -0
- coord/commands/plans.py +156 -0
- coord/commands/release.py +2232 -0
- coord/commands/report.py +341 -0
- coord/commands/review.py +1523 -0
- coord/commands/scorecard.py +252 -0
- coord/commands/sessions.py +1930 -0
- coord/commands/setup.py +576 -0
- coord/commands/status.py +2089 -0
- coord/commands/terminal.py +385 -0
- coord/commands/test_gate.py +775 -0
- coord/commands/tui.py +288 -0
- coord/comments.py +718 -0
- coord/config.py +3032 -0
- coord/conflict_fix.py +633 -0
- coord/dao.py +483 -0
- coord/dashboard/__init__.py +0 -0
- coord/dashboard/fixture.py +376 -0
- coord/dashboard/index.html +658 -0
- coord/dashboard/server.py +1894 -0
- coord/dashboard/terminal.py +382 -0
- coord/dashboard/webapp/.gitignore +9 -0
- coord/dashboard/webapp/components.json +17 -0
- coord/dashboard/webapp/dist/assets/Gallery-da3qNiIw.js +71 -0
- coord/dashboard/webapp/dist/assets/Terminal-9CEnUXvW.css +32 -0
- coord/dashboard/webapp/dist/assets/Terminal-skVFCxPU.js +63 -0
- coord/dashboard/webapp/dist/assets/index-DltfZR5f.js +184 -0
- coord/dashboard/webapp/dist/assets/index-Dq4kwTdw.css +1 -0
- coord/dashboard/webapp/dist/assets/workbox-window.prod.es5-BqEJf4Xk.js +2 -0
- coord/dashboard/webapp/dist/icons/icon-192.png +0 -0
- coord/dashboard/webapp/dist/icons/icon-512.png +0 -0
- coord/dashboard/webapp/dist/icons/icon.svg +5 -0
- coord/dashboard/webapp/dist/index.html +38 -0
- coord/dashboard/webapp/dist/manifest.webmanifest +1 -0
- coord/dashboard/webapp/dist/sw.js +1 -0
- coord/dashboard/webapp/dist/workbox-e4022e15.js +1 -0
- coord/dashboard/webapp/e2e/available-gates-terminal.spec.ts +75 -0
- coord/dashboard/webapp/e2e/deep-link.spec.ts +172 -0
- coord/dashboard/webapp/e2e/fixtureServer.ts +155 -0
- coord/dashboard/webapp/e2e/live-update-fixture.spec.ts +113 -0
- coord/dashboard/webapp/e2e/realtime.spec.ts +238 -0
- coord/dashboard/webapp/e2e/shell.spec.ts +309 -0
- coord/dashboard/webapp/e2e/smoke.spec.ts +191 -0
- coord/dashboard/webapp/e2e/terminal.spec.ts +420 -0
- coord/dashboard/webapp/e2e/theme.spec.ts +138 -0
- coord/dashboard/webapp/eslint.config.js +20 -0
- coord/dashboard/webapp/index.html +37 -0
- coord/dashboard/webapp/node_modules/flatted/python/flatted.py +144 -0
- coord/dashboard/webapp/package-lock.json +10584 -0
- coord/dashboard/webapp/package.json +63 -0
- coord/dashboard/webapp/playwright.acceptance.config.ts +166 -0
- coord/dashboard/webapp/playwright.config.ts +93 -0
- coord/dashboard/webapp/postcss.config.js +6 -0
- coord/dashboard/webapp/public/icons/icon-192.png +0 -0
- coord/dashboard/webapp/public/icons/icon-512.png +0 -0
- coord/dashboard/webapp/public/icons/icon.svg +5 -0
- coord/dashboard/webapp/src/App.tsx +140 -0
- coord/dashboard/webapp/src/api/client.ts +199 -0
- coord/dashboard/webapp/src/api/generated.ts +176 -0
- coord/dashboard/webapp/src/components/ConnectionBadge.tsx +52 -0
- coord/dashboard/webapp/src/components/Detail.tsx +800 -0
- coord/dashboard/webapp/src/components/Gallery.tsx +341 -0
- coord/dashboard/webapp/src/components/Home.tsx +435 -0
- coord/dashboard/webapp/src/components/MobileKeyBar.tsx +280 -0
- coord/dashboard/webapp/src/components/PanelHeader.tsx +59 -0
- coord/dashboard/webapp/src/components/PipelineCard.tsx +168 -0
- coord/dashboard/webapp/src/components/SessionCard.tsx +99 -0
- coord/dashboard/webapp/src/components/SessionDetail.tsx +140 -0
- coord/dashboard/webapp/src/components/SessionsList.tsx +81 -0
- coord/dashboard/webapp/src/components/Terminal.tsx +376 -0
- coord/dashboard/webapp/src/components/__tests__/ConnectionBadge.test.tsx +81 -0
- coord/dashboard/webapp/src/components/__tests__/Detail.test.tsx +680 -0
- coord/dashboard/webapp/src/components/__tests__/Gallery.test.tsx +83 -0
- coord/dashboard/webapp/src/components/__tests__/Home.test.tsx +271 -0
- coord/dashboard/webapp/src/components/__tests__/MobileKeyBar.test.tsx +197 -0
- coord/dashboard/webapp/src/components/__tests__/PipelineCard.test.tsx +143 -0
- coord/dashboard/webapp/src/components/__tests__/SessionCard.test.tsx +106 -0
- coord/dashboard/webapp/src/components/__tests__/Terminal.test.tsx +504 -0
- coord/dashboard/webapp/src/components/ui/badge.tsx +41 -0
- coord/dashboard/webapp/src/components/ui/button.tsx +54 -0
- coord/dashboard/webapp/src/components/ui/card.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/dialog.tsx +99 -0
- coord/dashboard/webapp/src/components/ui/dropdown-menu.tsx +189 -0
- coord/dashboard/webapp/src/components/ui/empty-state.tsx +35 -0
- coord/dashboard/webapp/src/components/ui/sheet.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/skeleton.tsx +9 -0
- coord/dashboard/webapp/src/components/ui/tabs.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/theme-provider.tsx +78 -0
- coord/dashboard/webapp/src/components/ui/theme-toggle.tsx +20 -0
- coord/dashboard/webapp/src/components/ui/toast.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/toaster.tsx +30 -0
- coord/dashboard/webapp/src/components/ui/tooltip.tsx +26 -0
- coord/dashboard/webapp/src/components/ui/use-toast.ts +134 -0
- coord/dashboard/webapp/src/index.css +210 -0
- coord/dashboard/webapp/src/lib/pipeline.ts +29 -0
- coord/dashboard/webapp/src/lib/utils.ts +6 -0
- coord/dashboard/webapp/src/main.tsx +46 -0
- coord/dashboard/webapp/src/realtime/RealtimeProvider.tsx +112 -0
- coord/dashboard/webapp/src/realtime/__tests__/RealtimeProvider.test.tsx +189 -0
- coord/dashboard/webapp/src/realtime/__tests__/connection.test.ts +255 -0
- coord/dashboard/webapp/src/realtime/connection.ts +227 -0
- coord/dashboard/webapp/src/realtime/events.ts +100 -0
- coord/dashboard/webapp/src/routes/__tests__/paths.test.ts +92 -0
- coord/dashboard/webapp/src/routes/paths.ts +92 -0
- coord/dashboard/webapp/src/shell/ActivityRail.tsx +335 -0
- coord/dashboard/webapp/src/shell/AppShell.tsx +276 -0
- coord/dashboard/webapp/src/shell/ComingSoon.tsx +33 -0
- coord/dashboard/webapp/src/shell/EmptyDetail.tsx +26 -0
- coord/dashboard/webapp/src/shell/RouteNotFound.tsx +33 -0
- coord/dashboard/webapp/src/shell/ShellLayout.tsx +147 -0
- coord/dashboard/webapp/src/shell/StatusBar.tsx +46 -0
- coord/dashboard/webapp/src/shell/__tests__/ShellLayout.test.tsx +520 -0
- coord/dashboard/webapp/src/shell/__tests__/shellState.test.ts +95 -0
- coord/dashboard/webapp/src/shell/__tests__/stubViewport.ts +40 -0
- coord/dashboard/webapp/src/shell/breakpoints.ts +87 -0
- coord/dashboard/webapp/src/shell/railItems.ts +105 -0
- coord/dashboard/webapp/src/shell/shellState.ts +174 -0
- coord/dashboard/webapp/src/shell/useRegionFocus.ts +95 -0
- coord/dashboard/webapp/src/test-setup.ts +41 -0
- coord/dashboard/webapp/src/vite-env.d.ts +2 -0
- coord/dashboard/webapp/tailwind.config.js +140 -0
- coord/dashboard/webapp/tsconfig.json +25 -0
- coord/dashboard/webapp/tsconfig.node.json +11 -0
- coord/dashboard/webapp/vite.config.ts +71 -0
- coord/db.py +1076 -0
- coord/dead_end.py +332 -0
- coord/deploy/README.md +33 -0
- coord/deploy/coord-agent.service +89 -0
- coord/deploy/coord-db-backup.service +60 -0
- coord/deploy/coord-db-backup.sh +74 -0
- coord/deploy/coord-db-backup.timer +18 -0
- coord/deploy/coord-drive-queue.service +117 -0
- coord/deploy/coord-drive-queue.timer +39 -0
- coord/deploy/coord-notify.service +48 -0
- coord/deploy/coord-notify.timer +24 -0
- coord/deploy/coord-release-propagate.service +83 -0
- coord/deploy/coord-release-propagate.timer +38 -0
- coord/deploy/coord-release-window.service +119 -0
- coord/deploy/coord-release-window.timer +36 -0
- coord/deploy/coord-serve.service +82 -0
- coord/deploy/coord-web-dist-build.service +43 -0
- coord/deploy/coord-web-dist-build.timer +36 -0
- coord/deploy/coord-web.service +125 -0
- coord/deploy_manifest.py +80 -0
- coord/deploy_units.py +384 -0
- coord/deps.py +115 -0
- coord/diagnose.py +1623 -0
- coord/dispatch.py +1009 -0
- coord/dist_name.py +123 -0
- coord/drive.py +3101 -0
- coord/drive_queue.py +2298 -0
- coord/drive_state.py +870 -0
- coord/events.py +381 -0
- coord/failure_class.py +914 -0
- coord/filelock.py +168 -0
- coord/fleet_config_health.py +300 -0
- coord/freshness.py +206 -0
- coord/gate_a.py +469 -0
- coord/gate_b.py +411 -0
- coord/gate_snapshot.py +385 -0
- coord/gates.py +582 -0
- coord/github_ops.py +1954 -0
- coord/goal.py +125 -0
- coord/graph_health.py +348 -0
- coord/health/__init__.py +69 -0
- coord/health/aggregate.py +129 -0
- coord/health/checks/__init__.py +13 -0
- coord/health/checks/agent_install.py +280 -0
- coord/health/checks/cargo_targets.py +171 -0
- coord/health/checks/claude_binary.py +65 -0
- coord/health/checks/deploy_lane_facts.py +458 -0
- coord/health/checks/disk.py +99 -0
- coord/health/checks/fleet_board.py +89 -0
- coord/health/checks/fleet_deploy_lanes.py +469 -0
- coord/health/checks/fleet_phantom.py +69 -0
- coord/health/checks/fleet_unit_drift.py +151 -0
- coord/health/checks/graph.py +192 -0
- coord/health/checks/plan_usage.py +88 -0
- coord/health/checks/repo_state.py +161 -0
- coord/health/checks/spawned_coord.py +465 -0
- coord/health/checks/timer_active.py +254 -0
- coord/health/checks/toolchain.py +547 -0
- coord/health/checks/unit_drift.py +648 -0
- coord/health/checks/unit_enablement.py +171 -0
- coord/health/checks/worktrees.py +96 -0
- coord/health/cli.py +121 -0
- coord/health/context.py +106 -0
- coord/health/fleet_snapshot.py +477 -0
- coord/health/models.py +250 -0
- coord/health/pypi.py +231 -0
- coord/health/registry.py +240 -0
- coord/health/render.py +82 -0
- coord/health/units.py +60 -0
- coord/hooks.py +106 -0
- coord/housekeeping.py +204 -0
- coord/interactive.py +4286 -0
- coord/issue_store.py +1496 -0
- coord/liveness_auditor.py +293 -0
- coord/machine_pause.py +755 -0
- coord/merge_queue.py +4681 -0
- coord/milestone_chat.py +600 -0
- coord/milestone_dispatch.py +943 -0
- coord/milestone_gate.py +709 -0
- coord/milestone_order.py +840 -0
- coord/mock_author.py +334 -0
- coord/models.py +891 -0
- coord/network.py +269 -0
- coord/new_issue_chat.py +229 -0
- coord/notify.py +3226 -0
- coord/openapi.py +404 -0
- coord/overlap_fence.py +133 -0
- coord/parentage.py +200 -0
- coord/parentage_github.py +58 -0
- coord/pipeline.py +481 -0
- coord/plan_parser.py +266 -0
- coord/plans.py +543 -0
- coord/platform_paths.py +43 -0
- coord/pr_body_lint.py +67 -0
- coord/prereqs.py +533 -0
- coord/progress.py +425 -0
- coord/providers/__init__.py +683 -0
- coord/providers/base.py +218 -0
- coord/providers/claude.py +284 -0
- coord/providers/claude_pty.py +610 -0
- coord/providers/opencode.py +896 -0
- coord/reconcile.py +2233 -0
- coord/refine_chat.py +485 -0
- coord/release_cordon.py +525 -0
- coord/release_propagate.py +1176 -0
- coord/release_verify.py +777 -0
- coord/release_window.py +322 -0
- coord/reports.py +1643 -0
- coord/revalidate.py +1101 -0
- coord/review.py +3317 -0
- coord/scorecard.py +484 -0
- coord/serve_app.py +7192 -0
- coord/skills/update-issue/SKILL.md +93 -0
- coord/smoke.py +1030 -0
- coord/split_work.py +210 -0
- coord/stage_projection.py +650 -0
- coord/state.py +5720 -0
- coord/test_author.py +1064 -0
- coord/test_chat.py +352 -0
- coord/test_orchestrator.py +494 -0
- coord/test_report.py +178 -0
- coord/tui_release.py +271 -0
- coord/usage.py +753 -0
- coord/usage_limits.py +358 -0
- coord/usage_rollup.py +709 -0
- coord/worker_events.py +954 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "coord-webapp",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc && vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"codegen": "cd ../../.. && .venv/bin/python scripts/codegen.py",
|
|
12
|
+
"codegen:check": "cd ../../.. && .venv/bin/python scripts/codegen.py --check",
|
|
13
|
+
"lint": "eslint src --report-unused-disable-directives --max-warnings 0",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"test:e2e": "playwright test",
|
|
17
|
+
"test:e2e:ui": "playwright test --ui",
|
|
18
|
+
"test:acceptance": "playwright test --config=playwright.acceptance.config.ts"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@radix-ui/react-dialog": "^1.1.23",
|
|
22
|
+
"@radix-ui/react-dropdown-menu": "^2.1.24",
|
|
23
|
+
"@radix-ui/react-slot": "^1.3.3",
|
|
24
|
+
"@radix-ui/react-tabs": "^1.1.21",
|
|
25
|
+
"@radix-ui/react-toast": "^1.2.23",
|
|
26
|
+
"@radix-ui/react-tooltip": "^1.2.16",
|
|
27
|
+
"@tanstack/react-query": "^5.62.2",
|
|
28
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
29
|
+
"@xterm/xterm": "^6.0.0",
|
|
30
|
+
"class-variance-authority": "^0.7.0",
|
|
31
|
+
"clsx": "^2.1.1",
|
|
32
|
+
"lucide-react": "^0.462.0",
|
|
33
|
+
"react": "^18.3.1",
|
|
34
|
+
"react-dom": "^18.3.1",
|
|
35
|
+
"react-router-dom": "^6.28.0",
|
|
36
|
+
"tailwind-merge": "^2.5.5",
|
|
37
|
+
"tailwindcss-animate": "^1.0.7"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@playwright/test": "^1.61.1",
|
|
41
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
42
|
+
"@testing-library/react": "^16.1.0",
|
|
43
|
+
"@testing-library/user-event": "^14.5.2",
|
|
44
|
+
"@types/node": "^26.1.2",
|
|
45
|
+
"@types/react": "^18.3.12",
|
|
46
|
+
"@types/react-dom": "^18.3.1",
|
|
47
|
+
"@typescript-eslint/eslint-plugin": "^8.14.0",
|
|
48
|
+
"@typescript-eslint/parser": "^8.14.0",
|
|
49
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
50
|
+
"autoprefixer": "^10.4.20",
|
|
51
|
+
"eslint": "^9.14.0",
|
|
52
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
53
|
+
"eslint-plugin-react-refresh": "^0.4.14",
|
|
54
|
+
"jsdom": "^25.0.1",
|
|
55
|
+
"postcss": "^8.4.47",
|
|
56
|
+
"tailwindcss": "^3.4.14",
|
|
57
|
+
"typescript": "^5.6.3",
|
|
58
|
+
"vite": "^5.4.10",
|
|
59
|
+
"vite-plugin-pwa": "^0.21.1",
|
|
60
|
+
"vitest": "^2.1.8",
|
|
61
|
+
"workbox-build": "^7.3.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright config for the SEALED oracle-loop acceptance suite (#1540),
|
|
3
|
+
* as opposed to `playwright.config.ts` (the `e2e/` smoke net — untouched,
|
|
4
|
+
* keeps running via `npm run test:e2e`).
|
|
5
|
+
*
|
|
6
|
+
* Why a sibling config rather than editing `playwright.config.ts` in place
|
|
7
|
+
* (docs/ORACLE_LOOP.md, #1552's "wiring failure, not a test failure" bug,
|
|
8
|
+
* arriving here in Playwright-shaped form):
|
|
9
|
+
*
|
|
10
|
+
* 1. `playwright.config.ts`'s `testDir: './e2e'` is invisible to anything
|
|
11
|
+
* under repo-root `tests/acceptance/ms-NN/` — a path filter pointing
|
|
12
|
+
* outside `testDir` matches zero files and Playwright exits 0 with 0
|
|
13
|
+
* tests. That is a silent, confidently-wrong green, not a red. This
|
|
14
|
+
* config's `testDir` points at the sealed tree instead so a slice
|
|
15
|
+
* actually gets discovered.
|
|
16
|
+
* 2. The acceptance run must select a machine-readable reporter, not
|
|
17
|
+
* inherit `e2e`'s CI-conditional `'github'`. In practice the
|
|
18
|
+
* `web-playwright` driver (#1539, coord/acceptance_drivers.py
|
|
19
|
+
* `_run_web_playwright`) always appends `--reporter=json` on the CLI,
|
|
20
|
+
* which overrides whatever `reporter:` this file declares — but this
|
|
21
|
+
* file still picks an explicit, non-CI-conditional default (`'list'`)
|
|
22
|
+
* so a human running it directly (outside the driver) gets readable
|
|
23
|
+
* output instead of silently inheriting a `CI` env var's default.
|
|
24
|
+
*
|
|
25
|
+
* `run:` (acceptance.drivers.claude-coordinator.routes, coordinator.yml):
|
|
26
|
+
* cd coord/dashboard/webapp && npm run test:acceptance -- {ms}
|
|
27
|
+
* `{ms}` (e.g. "ms-40") is substituted by `render_run_command` and passed as
|
|
28
|
+
* Playwright's positional test-file filter — it matches by substring against
|
|
29
|
+
* each spec file's resolved path, so `tests/acceptance/ms-40/foo.spec.ts`
|
|
30
|
+
* matches the filter `ms-40` without needing an exact relative path. This
|
|
31
|
+
* config also reads that same `ms-NN` token back out of `process.argv` (see
|
|
32
|
+
* `resolveFixturePath` below) to decide what to seed the webServer with.
|
|
33
|
+
*
|
|
34
|
+
* `entrypoint:` — deliberately NOT set on this route (#1552). Playwright
|
|
35
|
+
* discovers specs by walking `testDir`, exactly like `cli-pytest`'s
|
|
36
|
+
* `pytest tests/acceptance/{ms}` — there is no crate-root-style file that
|
|
37
|
+
* must `include!`/import each slice before it becomes reachable, so there is
|
|
38
|
+
* nothing to fold into `AcceptanceConfig.sealed_paths()`.
|
|
39
|
+
*
|
|
40
|
+
* `webServer` (#1818): boots a REAL `coord web --fixture <file> --dist dist`
|
|
41
|
+
* process (`coord/dashboard/fixture.py`, #1538) instead of the Vite dev
|
|
42
|
+
* server — deterministic reads with no daemon, no live fleet, no network,
|
|
43
|
+
* mirroring what `e2e/fixtureServer.ts` already does for
|
|
44
|
+
* `live-update-fixture.spec.ts`. `npm run build` runs first because
|
|
45
|
+
* `--dist` needs a production bundle on disk; both steps are chained into
|
|
46
|
+
* one `command` string so nothing outside this file has to remember to
|
|
47
|
+
* build before testing. `coord` must be resolvable on `$PATH` — true by
|
|
48
|
+
* construction here: this suite is only ever invoked by the `web-playwright`
|
|
49
|
+
* acceptance driver, which is itself `coord acceptance run`/`record`, so the
|
|
50
|
+
* `coord` CLI is always already installed on whatever machine reaches this
|
|
51
|
+
* config.
|
|
52
|
+
*
|
|
53
|
+
* Seeding convention for a slice (#1818): a milestone directory may declare
|
|
54
|
+
* `tests/acceptance/ms-NN/fixtures/<name>.json` — a `coord/dashboard/
|
|
55
|
+
* fixture.py`-shaped fixture, sealed alongside that milestone's
|
|
56
|
+
* `mocks/*.html`, so a slice's seed is part of the tree the test-author
|
|
57
|
+
* owns (see `coord.test_author.TEST_AUTHOR_SYSTEM_PROMPT`, which tells the
|
|
58
|
+
* next web test-author to write one of these instead of inventing an inline
|
|
59
|
+
* `page.route()` payload). Exactly one `*.json` file is expected per `ms-NN`
|
|
60
|
+
* directory; `resolveFixturePath` raises at config-load time if it finds
|
|
61
|
+
* more than one, rather than silently picking one and hiding the ambiguity.
|
|
62
|
+
* A milestone with no `fixtures/` directory falls back to the repo-wide
|
|
63
|
+
* `tests/fixtures/board-pipeline-basic.json` default (the same fixture
|
|
64
|
+
* `e2e/fixtureServer.ts` uses) — harmless for a slice that seeds itself
|
|
65
|
+
* another way.
|
|
66
|
+
*
|
|
67
|
+
* ms-51 (#1544's proving rig) is deliberately left on `page.route()` — see
|
|
68
|
+
* that slice's own header comment and #1818's "recommend leaving ms-51
|
|
69
|
+
* alone" — so it falls back to the default fixture above and ignores
|
|
70
|
+
* whatever it serves; ms-52 onward is where a slice is expected to declare
|
|
71
|
+
* its own `fixtures/*.json` and drop `page.route()` entirely. Known hazard
|
|
72
|
+
* ms-51 no longer inherits from a dev server (worth stating precisely,
|
|
73
|
+
* since a stale hazard note is worse than none): React 18 StrictMode's
|
|
74
|
+
* double-invoked effects were a *Vite dev mode* quirk, not a production
|
|
75
|
+
* bundle one, so a fixture-backed slice authored from here on doesn't need
|
|
76
|
+
* to write around it — but assertions should still prefer eventual state
|
|
77
|
+
* (`expect(...).toHaveText(...)`, `waitFor`) over exact effect/call counts,
|
|
78
|
+
* which is good practice regardless of backend.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
import { defineConfig, devices } from '@playwright/test'
|
|
82
|
+
import fs from 'node:fs'
|
|
83
|
+
import path from 'node:path'
|
|
84
|
+
import { fileURLToPath } from 'node:url'
|
|
85
|
+
|
|
86
|
+
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
87
|
+
const REPO_ROOT = path.resolve(here, '../../..')
|
|
88
|
+
const ACCEPTANCE_DIR = path.resolve(here, '../../../tests/acceptance')
|
|
89
|
+
const DIST_DIR = path.join(here, 'dist')
|
|
90
|
+
const DEFAULT_FIXTURE = path.join(REPO_ROOT, 'tests/fixtures/board-pipeline-basic.json')
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Resolve which fixture JSON seeds `coord web --fixture` for this run.
|
|
94
|
+
*
|
|
95
|
+
* The driver's `run:` command passes the target milestone as a positional
|
|
96
|
+
* filter (`npm run test:acceptance -- ms-51`), and Playwright forwards CLI
|
|
97
|
+
* args through verbatim, so `process.argv` already carries the `ms-NN`
|
|
98
|
+
* token this config needs — no second side-channel required. Falls back to
|
|
99
|
+
* `DEFAULT_FIXTURE` when no `ms-NN` arg is present (a bare `npm run
|
|
100
|
+
* test:acceptance` running the whole tree) or the matched milestone has no
|
|
101
|
+
* `fixtures/` directory of its own.
|
|
102
|
+
*/
|
|
103
|
+
function resolveFixturePath(): string {
|
|
104
|
+
const msArg = process.argv.find((a) => /^ms-\d+$/.test(a))
|
|
105
|
+
if (msArg) {
|
|
106
|
+
const fixturesDir = path.join(ACCEPTANCE_DIR, msArg, 'fixtures')
|
|
107
|
+
if (fs.existsSync(fixturesDir)) {
|
|
108
|
+
const files = fs
|
|
109
|
+
.readdirSync(fixturesDir)
|
|
110
|
+
.filter((f) => f.endsWith('.json'))
|
|
111
|
+
.sort()
|
|
112
|
+
if (files.length > 1) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${fixturesDir} declares ${files.length} fixture files ` +
|
|
115
|
+
`(${files.join(', ')}) — the acceptance webServer expects at ` +
|
|
116
|
+
'most one per ms-NN directory. Split into separate ms-NN ' +
|
|
117
|
+
'milestones instead of disambiguating fixtures within one.',
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
if (files.length === 1) return path.join(fixturesDir, files[0])
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return DEFAULT_FIXTURE
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const FIXTURE_PATH = resolveFixturePath()
|
|
127
|
+
|
|
128
|
+
export default defineConfig({
|
|
129
|
+
testDir: ACCEPTANCE_DIR,
|
|
130
|
+
testMatch: '**/*.spec.ts',
|
|
131
|
+
|
|
132
|
+
fullyParallel: false,
|
|
133
|
+
workers: 1,
|
|
134
|
+
|
|
135
|
+
forbidOnly: !!process.env.CI,
|
|
136
|
+
retries: process.env.CI ? 1 : 0,
|
|
137
|
+
|
|
138
|
+
// Explicit, non-CI-conditional default — see module docstring point 2.
|
|
139
|
+
// The web-playwright driver overrides this at invocation with its own
|
|
140
|
+
// `--reporter=json` CLI flag regardless.
|
|
141
|
+
reporter: 'list',
|
|
142
|
+
|
|
143
|
+
use: {
|
|
144
|
+
baseURL: 'http://localhost:5173',
|
|
145
|
+
screenshot: 'only-on-failure',
|
|
146
|
+
actionTimeout: 5_000,
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
projects: [
|
|
150
|
+
{
|
|
151
|
+
name: 'chromium',
|
|
152
|
+
use: { ...devices['Desktop Chrome'] },
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
|
|
156
|
+
webServer: {
|
|
157
|
+
command:
|
|
158
|
+
`npm run build && coord web --fixture "${FIXTURE_PATH}" ` +
|
|
159
|
+
`--dist "${DIST_DIR}" --host 127.0.0.1 --port 5173`,
|
|
160
|
+
url: 'http://localhost:5173',
|
|
161
|
+
reuseExistingServer: !process.env.CI,
|
|
162
|
+
// `npm run build` (tsc && vite build) plus process boot, comfortably
|
|
163
|
+
// over the old Vite-dev-only 30s budget.
|
|
164
|
+
timeout: 90_000,
|
|
165
|
+
},
|
|
166
|
+
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playwright E2E configuration for the coord webapp.
|
|
3
|
+
*
|
|
4
|
+
* Run: npm run test:e2e
|
|
5
|
+
*
|
|
6
|
+
* Prerequisites (one-time per machine):
|
|
7
|
+
* npx playwright install chromium
|
|
8
|
+
*
|
|
9
|
+
* Design:
|
|
10
|
+
* - The Vite dev server is started automatically by Playwright's
|
|
11
|
+
* `webServer` option, so no separate `npm run dev` is needed.
|
|
12
|
+
* - API calls (`/api/pipeline`, `/api/board`, …) are intercepted via
|
|
13
|
+
* `page.route()` inside each test — no live Python daemon required.
|
|
14
|
+
* This keeps the suite deterministic and fast. The one exception is
|
|
15
|
+
* `live-update-fixture.spec.ts` (#1551), which boots a real
|
|
16
|
+
* `coord web --fixture` process instead — see that file's header.
|
|
17
|
+
* - Only Chromium is targeted. Safari/Firefox variants belong in the
|
|
18
|
+
* coord/dashboard/webapp smoke_tests capability group; gate them on a
|
|
19
|
+
* machine with those browsers installed via coordinator.yml.
|
|
20
|
+
*
|
|
21
|
+
* Routing to capable hardware:
|
|
22
|
+
* coordinator.yml smoke_tests.capability_rules:
|
|
23
|
+
* - capability: browser
|
|
24
|
+
* paths: [coord/dashboard/webapp/**]
|
|
25
|
+
*
|
|
26
|
+
* `wide` / `narrow` projects (#1551, M-W1's exit gate): distinct Playwright
|
|
27
|
+
* *projects*, not `test.use({ viewport })` overrides inside `chromium` (which
|
|
28
|
+
* is how `shell.spec.ts` and `smoke.spec.ts` already cover breakpoints) —
|
|
29
|
+
* the story calls for both breakpoints as their own reportable line items.
|
|
30
|
+
* Scoped via `testMatch` to only the two files authored for it
|
|
31
|
+
* (`deep-link.spec.ts`, `theme.spec.ts`) and excluded from `chromium` via
|
|
32
|
+
* `testIgnore` so they don't triple-run; every other spec's breakpoint
|
|
33
|
+
* coverage (if any) stays exactly where it already was, under `chromium`.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { defineConfig, devices } from '@playwright/test'
|
|
37
|
+
|
|
38
|
+
const WIDE_VIEWPORT = { width: 1440, height: 900 }
|
|
39
|
+
const NARROW_VIEWPORT = { width: 390, height: 844 }
|
|
40
|
+
const BREAKPOINT_PROJECT_FILES = ['deep-link.spec.ts', 'theme.spec.ts']
|
|
41
|
+
|
|
42
|
+
export default defineConfig({
|
|
43
|
+
testDir: './e2e',
|
|
44
|
+
testMatch: '**/*.spec.ts',
|
|
45
|
+
|
|
46
|
+
// Run tests sequentially (single file, small suite for now).
|
|
47
|
+
fullyParallel: false,
|
|
48
|
+
workers: 1,
|
|
49
|
+
|
|
50
|
+
// Fail the build on CI if a test is `.only`-locked.
|
|
51
|
+
forbidOnly: !!process.env.CI,
|
|
52
|
+
|
|
53
|
+
// One retry on CI to guard against flaky timing.
|
|
54
|
+
retries: process.env.CI ? 1 : 0,
|
|
55
|
+
|
|
56
|
+
reporter: process.env.CI ? 'github' : 'list',
|
|
57
|
+
|
|
58
|
+
use: {
|
|
59
|
+
// Base URL is the Vite dev server started by webServer below.
|
|
60
|
+
baseURL: 'http://localhost:5173',
|
|
61
|
+
// Capture a screenshot on failure.
|
|
62
|
+
screenshot: 'only-on-failure',
|
|
63
|
+
// Short action timeout — these are simple DOM assertions.
|
|
64
|
+
actionTimeout: 5_000,
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
projects: [
|
|
68
|
+
{
|
|
69
|
+
name: 'chromium',
|
|
70
|
+
use: { ...devices['Desktop Chrome'] },
|
|
71
|
+
testIgnore: BREAKPOINT_PROJECT_FILES.map((f) => `**/${f}`),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'wide',
|
|
75
|
+
use: { ...devices['Desktop Chrome'], viewport: WIDE_VIEWPORT },
|
|
76
|
+
testMatch: BREAKPOINT_PROJECT_FILES,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'narrow',
|
|
80
|
+
use: { ...devices['Desktop Chrome'], viewport: NARROW_VIEWPORT },
|
|
81
|
+
testMatch: BREAKPOINT_PROJECT_FILES,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
|
|
85
|
+
// Start the Vite dev server before running tests.
|
|
86
|
+
// Playwright waits for the URL to respond before dispatching tests.
|
|
87
|
+
webServer: {
|
|
88
|
+
command: 'npm run dev',
|
|
89
|
+
url: 'http://localhost:5173',
|
|
90
|
+
reuseExistingServer: !process.env.CI,
|
|
91
|
+
timeout: 30_000,
|
|
92
|
+
},
|
|
93
|
+
})
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
|
2
|
+
<rect width="512" height="512" rx="80" fill="#1f6feb"/>
|
|
3
|
+
<text x="256" y="358" font-family="monospace,ui-monospace" font-size="320" font-weight="bold"
|
|
4
|
+
fill="white" text-anchor="middle">C</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { lazy, Suspense } from 'react'
|
|
2
|
+
import { BrowserRouter, Navigate, Routes, Route } from 'react-router-dom'
|
|
3
|
+
import Detail from '@/components/Detail'
|
|
4
|
+
import SessionDetail from '@/components/SessionDetail'
|
|
5
|
+
import { ShellLayout } from '@/shell/ShellLayout'
|
|
6
|
+
import { EmptyDetail } from '@/shell/EmptyDetail'
|
|
7
|
+
import { paths } from '@/routes/paths'
|
|
8
|
+
import { ThemeProvider } from '@/components/ui/theme-provider'
|
|
9
|
+
import { Toaster } from '@/components/ui/toaster'
|
|
10
|
+
|
|
11
|
+
// Lazy: xterm.js (#1068) is a sizable dependency that's only needed once a
|
|
12
|
+
// user actually taps into a live session's take-over view -- code-splitting
|
|
13
|
+
// it out of the main bundle keeps the Home/Detail initial load lean instead
|
|
14
|
+
// of shipping the terminal renderer to everyone up front.
|
|
15
|
+
const Terminal = lazy(() => import('@/components/Terminal'))
|
|
16
|
+
|
|
17
|
+
// Dev-only component gallery (#1546) — renders every ui/* primitive in both
|
|
18
|
+
// themes, so a human (or a Playwright acceptance slice) can see the whole
|
|
19
|
+
// system at once without hunting through the app for each one. Route-guarded
|
|
20
|
+
// by `import.meta.env.DEV` below so production can never navigate to it; it
|
|
21
|
+
// still ends up as its own lazy chunk in `dist/` (Rollup splits on the
|
|
22
|
+
// `import()` call site regardless of the guard), so vite.config.ts also
|
|
23
|
+
// excludes it from the PWA precache list -- see the comment there.
|
|
24
|
+
const Gallery = lazy(() => import('@/components/Gallery'))
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* App root. BrowserRouter is used here; the dashboard server serves index.html
|
|
28
|
+
* as a SPA fallback for all non-API paths so deep links work on hard reload.
|
|
29
|
+
*
|
|
30
|
+
* Route tree (#1548) — every screen the app can show gets one address:
|
|
31
|
+
*
|
|
32
|
+
* / -> redirect to /pipeline (replace, so
|
|
33
|
+
* it doesn't leave a dead entry back
|
|
34
|
+
* button lands on)
|
|
35
|
+
* /pipeline -> EmptyDetail in the detail slot
|
|
36
|
+
* /pipeline/:repo/:issue -> Detail (keyed on repo+issue, not
|
|
37
|
+
* assignment_id -- see Detail.tsx)
|
|
38
|
+
* /pipeline/:repo/:issue/:tab -> Detail, tab param threaded through
|
|
39
|
+
* for M-W2 to consume; round-trips
|
|
40
|
+
* today without changing what renders
|
|
41
|
+
* /sessions -> EmptyDetail
|
|
42
|
+
* /sessions/:id -> SessionDetail
|
|
43
|
+
* /board /machines /merge-queue
|
|
44
|
+
* /milestones /audit /spend
|
|
45
|
+
* /settings -> ComingSoon(view) -- placeholders for
|
|
46
|
+
* the M-W2+ panels, addressable today
|
|
47
|
+
* so a link to one isn't a 404 while
|
|
48
|
+
* it's being built
|
|
49
|
+
* * (inside the shell) -> RouteNotFound -- a real not-found
|
|
50
|
+
* state for a genuinely unknown path,
|
|
51
|
+
* rendered inside the shell (rail,
|
|
52
|
+
* status bar) rather than a blank page
|
|
53
|
+
*
|
|
54
|
+
* Every route below `/pipeline` is a child of `ShellLayout`, the react-router
|
|
55
|
+
* *layout route*: the child fills the detail slot (rail + list + detail on
|
|
56
|
+
* wide, the phone app on narrow) and `ShellLayout` derives the rail selection
|
|
57
|
+
* and the list panel's content from the URL itself (`shellViewFromPath`)
|
|
58
|
+
* rather than from separately persisted state -- see `shellState.ts`'s doc
|
|
59
|
+
* comment.
|
|
60
|
+
*
|
|
61
|
+
* The `/` redirect is deliberately declared *outside* `ShellLayout`, as a
|
|
62
|
+
* sibling of it rather than one of its children. `AppShell` only mounts the
|
|
63
|
+
* detail slot -- the thing that would render a child route's `element` --
|
|
64
|
+
* when `showDetail` is true, and on narrow that's `false` until an item is
|
|
65
|
+
* selected (`detailActive`). A redirect placed under `ShellLayout` therefore
|
|
66
|
+
* silently never fires on a phone-sized cold load at `/`: `Outlet` is never
|
|
67
|
+
* mounted, so `<Navigate>`'s effect never runs, and the shell renders with no
|
|
68
|
+
* matched child at all -- exactly the "genuinely unknown route" case, so it
|
|
69
|
+
* falls through to the `*` not-found state instead of redirecting. Matching
|
|
70
|
+
* `/` *before* composing the shell sidesteps the whole class of bug: nothing
|
|
71
|
+
* about "should this redirect fire" may depend on shell layout state.
|
|
72
|
+
*
|
|
73
|
+
* `/terminal/:sessionId` stays deliberately *outside* the shell for a
|
|
74
|
+
* different reason: the PTY pane wants the whole viewport and brings its own
|
|
75
|
+
* key bar, and framing a terminal in a rail plus a status bar would cost it
|
|
76
|
+
* rows on exactly the device (a phone) where rows are scarcest.
|
|
77
|
+
*/
|
|
78
|
+
export default function App() {
|
|
79
|
+
return (
|
|
80
|
+
<ThemeProvider>
|
|
81
|
+
<BrowserRouter>
|
|
82
|
+
<Routes>
|
|
83
|
+
<Route path="/" element={<Navigate to={paths.pipeline()} replace />} />
|
|
84
|
+
|
|
85
|
+
<Route element={<ShellLayout />}>
|
|
86
|
+
<Route path="/pipeline" element={<EmptyDetail />} />
|
|
87
|
+
<Route path="/pipeline/:repo/:issue" element={<Detail />} />
|
|
88
|
+
<Route path="/pipeline/:repo/:issue/:tab" element={<Detail />} />
|
|
89
|
+
|
|
90
|
+
<Route path="/sessions" element={<EmptyDetail />} />
|
|
91
|
+
<Route path="/sessions/:id" element={<SessionDetail />} />
|
|
92
|
+
|
|
93
|
+
{/* Placeholders for the M-W2+ panels (#1548) -- addressable now,
|
|
94
|
+
not a 404, so a bookmark or a pasted link survives the panel
|
|
95
|
+
shipping later. The rail marks each of these 'soon' and won't
|
|
96
|
+
navigate here on a click (railItems.ts); this is what a typed
|
|
97
|
+
or pasted link to one lands on in the meantime. */}
|
|
98
|
+
<Route path="/board" element={null} />
|
|
99
|
+
<Route path="/machines" element={null} />
|
|
100
|
+
<Route path="/merge-queue" element={null} />
|
|
101
|
+
<Route path="/milestones" element={null} />
|
|
102
|
+
<Route path="/audit" element={null} />
|
|
103
|
+
<Route path="/spend" element={null} />
|
|
104
|
+
<Route path="/settings" element={null} />
|
|
105
|
+
|
|
106
|
+
{/* Genuinely unknown path under the shell -- a typo'd URL, or a
|
|
107
|
+
link to a feature that no longer exists. `ShellLayout` puts
|
|
108
|
+
`RouteNotFound` in the *list* slot for this case (there is no
|
|
109
|
+
"current view" to hand a list to); this route's own element
|
|
110
|
+
stays empty so wide doesn't show the same message twice. */}
|
|
111
|
+
<Route path="*" element={null} />
|
|
112
|
+
</Route>
|
|
113
|
+
<Route
|
|
114
|
+
path="/terminal/:sessionId"
|
|
115
|
+
element={
|
|
116
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
117
|
+
<Suspense fallback={null}>
|
|
118
|
+
<Terminal />
|
|
119
|
+
</Suspense>
|
|
120
|
+
</div>
|
|
121
|
+
}
|
|
122
|
+
/>
|
|
123
|
+
{import.meta.env.DEV && (
|
|
124
|
+
<Route
|
|
125
|
+
path="/gallery"
|
|
126
|
+
element={
|
|
127
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
128
|
+
<Suspense fallback={null}>
|
|
129
|
+
<Gallery />
|
|
130
|
+
</Suspense>
|
|
131
|
+
</div>
|
|
132
|
+
}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
</Routes>
|
|
136
|
+
</BrowserRouter>
|
|
137
|
+
<Toaster />
|
|
138
|
+
</ThemeProvider>
|
|
139
|
+
)
|
|
140
|
+
}
|