@hachej/boring-workspace 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/CodeEditor-DQqOn4xz.js +266 -0
- package/dist/CommandPalette-aM61U-b0.js +5229 -0
- package/dist/FileTree-DRq_bfue.js +245 -0
- package/dist/MarkdownEditor-DjiHxnRv.js +349 -0
- package/dist/WorkspaceLoadingState-By0dZoPD.js +568 -0
- package/dist/agent-tool-NvxKfist.d.ts +28 -0
- package/dist/app-front.d.ts +485 -0
- package/dist/app-front.js +452 -0
- package/dist/app-server.d.ts +53 -0
- package/dist/app-server.js +769 -0
- package/dist/bootstrapServer-BRUqUpVW.d.ts +66 -0
- package/dist/boring-workspace.css +1 -0
- package/dist/charts.d.ts +114 -0
- package/dist/charts.js +143 -0
- package/dist/events.d.ts +178 -0
- package/dist/events.js +88 -0
- package/dist/explorer-DtLUnuah.d.ts +129 -0
- package/dist/panel-DnvDNQac.js +6 -0
- package/dist/server.d.ts +84 -0
- package/dist/server.js +811 -0
- package/dist/shared.d.ts +113 -0
- package/dist/shared.js +11 -0
- package/dist/testing-e2e.d.ts +68 -0
- package/dist/testing-e2e.js +45 -0
- package/dist/testing.d.ts +464 -0
- package/dist/testing.js +10984 -0
- package/dist/utils-B6yFEsav.js +8 -0
- package/dist/workspace.css +5780 -0
- package/dist/workspace.d.ts +2119 -0
- package/dist/workspace.js +1884 -0
- package/docs/INTERFACES.md +58 -0
- package/docs/PLUGIN_STRUCTURE.md +162 -0
- package/docs/README.md +19 -0
- package/docs/bridge.md +135 -0
- package/docs/panels.md +102 -0
- package/docs/plans/GENERIC_EXPLORER_PLUGIN_PLAN.md +455 -0
- package/docs/plans/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md +962 -0
- package/docs/plans/PLUGIN_OUTPUTS_ISOLATION_PLAN.md +301 -0
- package/docs/plans/README.md +9 -0
- package/docs/plans/UI_BRIDGE_OWNERSHIP_REFACTOR.md +303 -0
- package/docs/plans/archive/CODE_OWNERSHIP_CLEANUP_PLAN.md +387 -0
- package/docs/plans/archive/COMMAND_PALETTE_REGISTRY.md +814 -0
- package/docs/plans/archive/DECLARATIVE_LAYOUT_MIGRATION.md +277 -0
- package/docs/plans/archive/PLUGIN_MODEL.md +3674 -0
- package/docs/plans/archive/SRC_FOLDER_REORG_PLAN.md +307 -0
- package/docs/plans/archive/UNIFIED_EVENT_BUS.md +647 -0
- package/docs/plans/archive/WORKSPACE_V2_PLAN.md +2489 -0
- package/docs/plugins.md +158 -0
- package/package.json +164 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Code ownership cleanup — keep packages reusable, apps lean
|
|
2
|
+
|
|
3
|
+
**Status:** draft
|
|
4
|
+
**Owners:** workspace, agent
|
|
5
|
+
**Last updated:** 2026-04-28
|
|
6
|
+
|
|
7
|
+
## Problem
|
|
8
|
+
|
|
9
|
+
Tests for the `@boring/workspace` chat shell, dock, UI bridge, and `@boring/agent`
|
|
10
|
+
plumbing currently live in the standalone macro repo's `e2e/` directory (29
|
|
11
|
+
specs — see [`CONSOLIDATE_AND_STANDALONIZE.md`](../../../apps/boring-macro-v2/docs/CONSOLIDATE_AND_STANDALONIZE.md)
|
|
12
|
+
for how that repo merges into `apps/boring-macro-v2/e2e/`). That happened
|
|
13
|
+
because the macro app was the first non-trivial consumer and the tests caught real
|
|
14
|
+
bugs there. But the contents are mixed:
|
|
15
|
+
|
|
16
|
+
- Some specs assert macro-only things (FRED catalog, ChartCanvasPane tabs, DeckPane
|
|
17
|
+
edit/save, `/api/macro/*`).
|
|
18
|
+
- Others test things that should be true for every app built on `@boring/workspace`
|
|
19
|
+
(composer border state, dockview split-shrink, sidebar persistence keys,
|
|
20
|
+
ChatCenteredShell `appTitle`, the bridge accepting `openPanel`).
|
|
21
|
+
|
|
22
|
+
Two anti-patterns this creates:
|
|
23
|
+
|
|
24
|
+
1. **Generic regressions only get caught by macro.** If a developer breaks
|
|
25
|
+
`SurfaceShell`'s persisted-width hydration or `ChatCenteredShell.appTitle`, the
|
|
26
|
+
first signal is a macro test failing — even though no macro code changed. The
|
|
27
|
+
blame trail and reproduction loop go through macro instead of workspace.
|
|
28
|
+
2. **The playground accumulates app-shaped tests.** When we move generic specs
|
|
29
|
+
into `apps/workspace-playground/e2e/`, the playground's job creeps from "demo
|
|
30
|
+
the chat shell with a couple of mock adapters" to "be the test harness for
|
|
31
|
+
`@boring/workspace`." That's a different artifact: a demo wants to be small and
|
|
32
|
+
readable; a test harness wants to be deterministic and exhaustive. Stuffing
|
|
33
|
+
them into one app makes both worse.
|
|
34
|
+
|
|
35
|
+
We also currently lack a place to test combinations that aren't representative of
|
|
36
|
+
any one app — e.g. `extraPanels` filtering, the bridge dispatcher's
|
|
37
|
+
`!surface ? return : run` early-out, two-instance ChatCenteredShells with
|
|
38
|
+
distinct `storageKey`s. None of these belong in macro or in the playground.
|
|
39
|
+
|
|
40
|
+
## Goal
|
|
41
|
+
|
|
42
|
+
Tests for `@boring/workspace` and `@boring/agent` primitives live INSIDE the
|
|
43
|
+
package they test, not in a downstream app. Child apps ship only the specs that
|
|
44
|
+
exercise app-specific contracts.
|
|
45
|
+
|
|
46
|
+
Also: define a concrete repo-wide cleanup boundary so code placement stays sane:
|
|
47
|
+
|
|
48
|
+
- `packages/*` = reusable primitives, adapters, and framework helpers.
|
|
49
|
+
- `apps/*` = composition, branding, product/domain logic, and deploy wiring.
|
|
50
|
+
- `apps/workspace-playground` remains demo-first and intentionally lean.
|
|
51
|
+
|
|
52
|
+
Concretely:
|
|
53
|
+
|
|
54
|
+
| Lives in… | Tests… | Hits… |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `packages/workspace/e2e/` | chat shell layout, dock split/resize, UI bridge dispatcher, sidebar persistence, composer styling | Browser against a **fixture app** in `packages/workspace/e2e/fixture/` |
|
|
57
|
+
| `packages/agent/__tests__/` (existing) | `AgentTool` schema, harness, chat route, sessions | In-process |
|
|
58
|
+
| `apps/workspace-playground/` | nothing — playground stays a demo | n/a |
|
|
59
|
+
| `apps/boring-macro-v2/e2e/` | macro adapter shape, FRED catalog routes, ChartCanvasPane tabs, DeckPane edit/save, macro tool catalog | Browser against the macro dev server |
|
|
60
|
+
|
|
61
|
+
## Why a fixture, not the playground
|
|
62
|
+
|
|
63
|
+
The playground's `App.tsx` exists to demonstrate the package's public API to a
|
|
64
|
+
human reading the source. It's tuned for readability:
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<ChatCenteredShell
|
|
68
|
+
appTitle="Boring"
|
|
69
|
+
data={dataPaneConfig}
|
|
70
|
+
onSurfaceReady={(api) => { surfaceRef.current = api }}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A test harness wants different things — known-fixed mock data, a stable storage
|
|
75
|
+
key namespace, deterministic agent responses, hooks for `data-testid`, easy
|
|
76
|
+
two-tab side-by-side scenarios. Adding any of those to the playground hurts its
|
|
77
|
+
"read this to learn the API" job. Forking a small fixture under
|
|
78
|
+
`packages/workspace/e2e/fixture/` lets the playground stay clean.
|
|
79
|
+
|
|
80
|
+
The fixture is roughly:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
packages/workspace/e2e/
|
|
84
|
+
fixture/
|
|
85
|
+
index.html
|
|
86
|
+
main.tsx
|
|
87
|
+
App.tsx # uses every public API surface we test, with stable test ids
|
|
88
|
+
mockSeriesAdapter.ts # deterministic, no random IDs
|
|
89
|
+
mockSessions.ts
|
|
90
|
+
helpers/
|
|
91
|
+
boot.ts # bootClean(page, seed) — same shape as macro's
|
|
92
|
+
pane.ts # openPaneViaBridge wait-for-tab pattern
|
|
93
|
+
specs/
|
|
94
|
+
composer-border.spec.ts
|
|
95
|
+
chat-shell-topbar.spec.ts
|
|
96
|
+
chat-suggestions.spec.ts # asserts defaultChatSuggestions render
|
|
97
|
+
dock-split-shrink.spec.ts
|
|
98
|
+
extra-panels.spec.ts
|
|
99
|
+
layout-persistence.spec.ts
|
|
100
|
+
bridge-openpanel.spec.ts
|
|
101
|
+
bridge-openfile.spec.ts
|
|
102
|
+
bridge-no-surface-noop.spec.ts # the early-out we hit
|
|
103
|
+
playwright.config.ts # webServer: vite serves fixture/ on a sibling port
|
|
104
|
+
package.json # internal — `pnpm --filter @boring/workspace test:e2e`
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The fixture is a real Vite app (because we need a real browser + real dockview),
|
|
108
|
+
but it's not exported from the package and never published. It runs only when
|
|
109
|
+
`pnpm test:e2e` is invoked from the workspace package.
|
|
110
|
+
|
|
111
|
+
## Why not extend `boring-ui-v2/e2e/`
|
|
112
|
+
|
|
113
|
+
The repo root already has `boring-ui-v2/e2e/` with its own playwright config and
|
|
114
|
+
`spawnBackend` fixtures (`fixtures.ts`). That suite tests the agent backend with
|
|
115
|
+
isolated tmp workspaces and is geared toward **headless API + harness** behavior
|
|
116
|
+
(sessions persistence, mode flips, bridge protocol). It does not boot a frontend.
|
|
117
|
+
|
|
118
|
+
Mixing browser-driven UI specs into that infrastructure would duplicate Vite
|
|
119
|
+
boot logic and conflate "test the backend in isolation" with "test the chat
|
|
120
|
+
shell in a real DOM." Two suites with two configs and two purposes is cleaner:
|
|
121
|
+
|
|
122
|
+
- `boring-ui-v2/e2e/` — backend-focused, in-process, no browser, no Vite.
|
|
123
|
+
- `packages/workspace/e2e/` — browser-focused, real Vite + fixture app, real
|
|
124
|
+
dockview + recharts.
|
|
125
|
+
|
|
126
|
+
Each can evolve at its own pace.
|
|
127
|
+
|
|
128
|
+
## Repository findings (2026-04-28 scan)
|
|
129
|
+
|
|
130
|
+
### A) `apps/boring-macro-v2` (child app)
|
|
131
|
+
|
|
132
|
+
**Keep in app (macro-specific):**
|
|
133
|
+
|
|
134
|
+
- `src/server/routes/billing.ts` (Stripe tiers, checkout/webhook, quota policy)
|
|
135
|
+
- `src/server/routes/waitlist.ts` (macro landing behavior)
|
|
136
|
+
- macro tool semantics and system prompt in `src/server/tools/macroTools.ts`
|
|
137
|
+
- macro env policy in `src/server/config.ts` (`BM_*`, FRED defaults)
|
|
138
|
+
|
|
139
|
+
**Extract to reusable package surfaces:**
|
|
140
|
+
|
|
141
|
+
- `src/server/services/tabBus.ts` → **delete, not extract.** The
|
|
142
|
+
`@boring/workspace` UI bridge (`exec_ui` + `openPanel` command) already
|
|
143
|
+
covers everything tabBus does (push a "show this series" command from
|
|
144
|
+
the agent to the workbench). Migrate the macro call sites that still
|
|
145
|
+
push to tabBus over to `bridge.postCommand({kind:"openPanel", ...})`
|
|
146
|
+
and delete the file. Adding tabBus's API to workspace would duplicate
|
|
147
|
+
the bridge surface.
|
|
148
|
+
- reusable pieces from `src/server/services/clickhouse.ts`
|
|
149
|
+
(generic CH adapter/cache/read-only SQL guard pattern)
|
|
150
|
+
- reusable queue/rate-limit scaffolding from `src/server/services/fredRefresh.ts`
|
|
151
|
+
(keep FRED provider app-local; move generic queue core)
|
|
152
|
+
- small parsing helpers in `routes/macro.ts` (`clampInt`, `optionalInt`, etc.)
|
|
153
|
+
|
|
154
|
+
### B) `apps/full-app` (integration app)
|
|
155
|
+
|
|
156
|
+
**Keep in app:**
|
|
157
|
+
|
|
158
|
+
- composition UI (`src/front/main.tsx`) and app branding
|
|
159
|
+
- deployment/runtime entry wiring
|
|
160
|
+
|
|
161
|
+
**Extract to `@boring/core/server` helpers:**
|
|
162
|
+
|
|
163
|
+
- auth proxy forwarding pattern from `src/server/main.ts`
|
|
164
|
+
- SPA static fallback + safe path checks
|
|
165
|
+
- CSP nonce HTML injector utility
|
|
166
|
+
- mixed `/auth/*` API-vs-frontend routing pattern
|
|
167
|
+
|
|
168
|
+
### C) `apps/workspace-playground` (demo app)
|
|
169
|
+
|
|
170
|
+
**Keep in app:**
|
|
171
|
+
|
|
172
|
+
- showcase seed data/messages/session stories
|
|
173
|
+
|
|
174
|
+
**Move/replace with package utilities:**
|
|
175
|
+
|
|
176
|
+
- dedupe `apps/workspace-playground/src/mockApi.ts` against
|
|
177
|
+
`packages/workspace/src/testing/mockApi.ts`
|
|
178
|
+
- avoid growing app-local infra for fake FS/api when package testing utility
|
|
179
|
+
already exists
|
|
180
|
+
|
|
181
|
+
### D) `packages/workspace` + `packages/agent`
|
|
182
|
+
|
|
183
|
+
- own all generic UI bridge behavior + its e2e/spec harness
|
|
184
|
+
- own generic chat-shell/dock persistence behavior tests
|
|
185
|
+
- avoid depending on macro for regressions in generic surfaces
|
|
186
|
+
|
|
187
|
+
## Migration plan
|
|
188
|
+
|
|
189
|
+
### Phase 0 — Build the workspace `./server` and `./shared` entries (prerequisite)
|
|
190
|
+
|
|
191
|
+
`packages/workspace/package.json` declares five subpath exports:
|
|
192
|
+
|
|
193
|
+
```jsonc
|
|
194
|
+
{
|
|
195
|
+
".": "./dist/workspace.js", // main API (ChatCenteredShell, panes, hooks, …)
|
|
196
|
+
"./testing": "./dist/testing.js", // test helpers (TestWorkspaceProvider, mocks)
|
|
197
|
+
"./ui-shadcn": "./dist/ui-shadcn.js", // shadcn primitives (Button, Card, …)
|
|
198
|
+
"./shared": "./dist/shared.js", // UiBridge / UiCommand / UiState types
|
|
199
|
+
"./server": "./dist/server.js" // createWorkspaceAgentApp, uiRoutes, uiTools
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
…but `vite.config.ts`'s `build.lib.entry` only emits **three** of them:
|
|
204
|
+
`workspace`, `testing`, `ui-shadcn`. The `./server` and `./shared`
|
|
205
|
+
entries are declared in the package.json but never produced — so any
|
|
206
|
+
consumer outside the monorepo (or any tool that bypasses Vite's path
|
|
207
|
+
aliases) gets a broken import.
|
|
208
|
+
|
|
209
|
+
This is why earlier work had to **inline `createWorkspaceAgentApp` into
|
|
210
|
+
the consuming app** (`boring-macro-v2/src/server/uiBridge.ts` is a
|
|
211
|
+
copy-paste of `packages/workspace/src/server/createWorkspaceAgentApp.ts`
|
|
212
|
+
+ `uiTools.ts` + `uiRoutes.ts`). Phase 1's "import from
|
|
213
|
+
`@boring/workspace/server`" doesn't actually work today; it has to be
|
|
214
|
+
made to work first.
|
|
215
|
+
|
|
216
|
+
**Tasks:**
|
|
217
|
+
|
|
218
|
+
1. Add `tsup` to `packages/workspace/devDependencies` (already in
|
|
219
|
+
`agent` and `core` — copy that pattern).
|
|
220
|
+
2. Add a `tsup.config.ts` that emits `dist/server.js` + `dist/server.d.ts`
|
|
221
|
+
from `src/server/index.ts`, and `dist/shared.js` + `dist/shared.d.ts`
|
|
222
|
+
from `src/shared/index.ts`.
|
|
223
|
+
3. Update `package.json` `build` script:
|
|
224
|
+
`"build": "tsup && vite build"` (mirrors `agent`).
|
|
225
|
+
4. Add `assert-build-artifacts.mjs` parity check (`agent` already has
|
|
226
|
+
one) so a missing entry fails the build instead of shipping a broken
|
|
227
|
+
package.
|
|
228
|
+
5. Confirm `pnpm --filter @boring/workspace build` produces all five
|
|
229
|
+
entries declared in the exports map.
|
|
230
|
+
6. Delete `boring-macro-v2/src/server/uiBridge.ts` and replace with
|
|
231
|
+
`import { createWorkspaceAgentApp } from "@boring/workspace/server"`.
|
|
232
|
+
This is the ground-truth verification that the build works.
|
|
233
|
+
|
|
234
|
+
Phase 0 is a prerequisite for Phase 1 (the fixture would otherwise have
|
|
235
|
+
to inline the same code), Phase 4 (extractions that target package
|
|
236
|
+
surfaces require the targets to actually be buildable), and the macro
|
|
237
|
+
consolidation work in
|
|
238
|
+
[`apps/boring-macro-v2/docs/CONSOLIDATE_AND_STANDALONIZE.md`](../../../apps/boring-macro-v2/docs/CONSOLIDATE_AND_STANDALONIZE.md)
|
|
239
|
+
(its Phase C drops the inlined `uiBridge.ts` workaround).
|
|
240
|
+
|
|
241
|
+
Estimated cost: half a day. Adds zero behavioural change — just makes
|
|
242
|
+
the package's declared API match what's shipped.
|
|
243
|
+
|
|
244
|
+
### Phase 1 — Set up the workspace fixture (one PR)
|
|
245
|
+
|
|
246
|
+
1. `packages/workspace/e2e/fixture/{index.html, main.tsx, App.tsx}` — minimal
|
|
247
|
+
ChatCenteredShell with a tiny mock series adapter and 2-3 panel registrations
|
|
248
|
+
(`code-editor`, `markdown-editor`, plus a stub `chart-canvas` that renders a
|
|
249
|
+
recharts `LineChart` against synthetic data so dockview-shrink tests have
|
|
250
|
+
something to clip).
|
|
251
|
+
2. `packages/workspace/e2e/playwright.config.ts` — webServer boots Vite on
|
|
252
|
+
`localhost:5300` (sibling to whatever else may be running).
|
|
253
|
+
3. `packages/workspace/e2e/helpers/{boot.ts, pane.ts}` — port `bootClean` and
|
|
254
|
+
the `openPaneViaBridge` waiter from `boring-macro-v2/e2e/helpers.ts`,
|
|
255
|
+
parameterized on storage key.
|
|
256
|
+
4. `pnpm --filter @boring/workspace test:e2e` script.
|
|
257
|
+
5. Add fixture-only deterministic adapters; do **not** reuse playground app code.
|
|
258
|
+
|
|
259
|
+
### Phase 2 — Move generic specs from boring-macro
|
|
260
|
+
|
|
261
|
+
> **Coordination with the macro consolidation plan:** the 29 specs live in
|
|
262
|
+
> the *standalone* macro today. The macro plan's Phase B copies them into
|
|
263
|
+
> `apps/boring-macro-v2/e2e/`. Sequencing: do macro Phase B FIRST (move
|
|
264
|
+
> 29 specs in), then this Phase 2 (split — generic ones move to
|
|
265
|
+
> `packages/workspace/e2e/`, leaving ~14 macro-only). This avoids
|
|
266
|
+
> retroactively touching standalone files we're about to archive.
|
|
267
|
+
|
|
268
|
+
Specs that come over (one per file, lightly adapted to the fixture's storage key
|
|
269
|
+
and pane registry):
|
|
270
|
+
|
|
271
|
+
- `composer-border.spec.ts` — direct copy.
|
|
272
|
+
- `topbar.spec.ts` → `chat-shell-topbar.spec.ts` — assert `appTitle` prop is
|
|
273
|
+
rendered. Adapted from `"boring.macro"` to `"Workspace"` (whatever the
|
|
274
|
+
fixture sets).
|
|
275
|
+
- `split-no-clip.spec.ts` → `dock-split-shrink.spec.ts` — open two panes, drag
|
|
276
|
+
bottom split, measure that the recharts wrapper inside the second group
|
|
277
|
+
doesn't overflow. Uses the fixture's stub chart pane (no FRED).
|
|
278
|
+
- `layout-persistence.spec.ts` — sidebar collapsed/width persistence under
|
|
279
|
+
`<storageKey>:surface:*`, drawer/surface flags under `<storageKey>:*`.
|
|
280
|
+
- `agent.spec.ts` (the workspace half) → `bridge-openpanel.spec.ts` /
|
|
281
|
+
`bridge-no-surface-noop.spec.ts` — `POST /api/v1/ui/commands` accepts
|
|
282
|
+
`openPanel`, dispatcher noop's when no surface mounted, dispatch fires when it
|
|
283
|
+
does.
|
|
284
|
+
|
|
285
|
+
After Phase 2, `apps/boring-macro-v2/e2e/` shrinks from 29 → ~14 specs:
|
|
286
|
+
- `chat-suggestions.spec.ts` (macro labels)
|
|
287
|
+
- `catalog.spec.ts` (FRED catalog UI + 87k count + macro routes)
|
|
288
|
+
- `catalog-to-chart.spec.ts` (macro adapter onActivate → chart-canvas)
|
|
289
|
+
- `chart-tabs.spec.ts` (ChartCanvasPane Chart/Table/Metadata/Lineage + macro routes)
|
|
290
|
+
- `deck.spec.ts` (DeckPane + `/api/macro/deck/*`)
|
|
291
|
+
- `agent.spec.ts` (trimmed to assert macro tool catalog: `execute_sql`,
|
|
292
|
+
`macro_search`, `get_series_data`, `persist_derived_series`)
|
|
293
|
+
|
|
294
|
+
### Phase 3 — Demo/app boundary cleanup (parallel with e2e migration)
|
|
295
|
+
|
|
296
|
+
1. Replace playground-local FS mock backend with `@boring/workspace/testing`
|
|
297
|
+
equivalent (or extract common shared helper then consume from both).
|
|
298
|
+
2. Keep playground focused on readable composition examples; move test harness
|
|
299
|
+
glue into `packages/workspace/e2e/fixture`.
|
|
300
|
+
3. Add boundary guardrails:
|
|
301
|
+
- app-level domain terms (e.g. macro billing/waitlist/FRED policy) never land
|
|
302
|
+
in package runtime paths;
|
|
303
|
+
- generic dock/chat/bridge behaviors never rely on macro app tests.
|
|
304
|
+
|
|
305
|
+
### Phase 4 — Shared helper extraction from apps
|
|
306
|
+
|
|
307
|
+
> **Gating:** `@boring/core` is currently WIP — its public surface isn't
|
|
308
|
+
> frozen. Don't promote into a moving target. Until core lands, the
|
|
309
|
+
> reusable bits below live in `apps/full-app/src/server/_shared/` (or
|
|
310
|
+
> equivalent app-local location) and migrate to `@boring/core/server` in
|
|
311
|
+
> a single follow-up PR once core's API stabilises. The review checklist
|
|
312
|
+
> in **Enforcement** below should reject premature `@boring/core/server`
|
|
313
|
+
> promotion until that gate clears.
|
|
314
|
+
|
|
315
|
+
1. Stage reusable full-app server glue (auth proxy, static SPA fallback,
|
|
316
|
+
CSP nonce HTML transforms) in `apps/full-app/src/server/_shared/`.
|
|
317
|
+
Promote to `@boring/core/server` once core stabilises.
|
|
318
|
+
2. Replace macro `tabBus.ts` with `bridge.postCommand({kind:"openPanel", ...})`
|
|
319
|
+
call sites — no extraction needed; the workspace UI bridge already
|
|
320
|
+
owns this surface.
|
|
321
|
+
3. Extract generic queue/rate-limit primitive used by macro refresh
|
|
322
|
+
workflows into a package utility (provider-specific fetchers remain
|
|
323
|
+
app-owned). Initial home: `apps/boring-macro-v2/src/server/_shared/`,
|
|
324
|
+
promote later.
|
|
325
|
+
|
|
326
|
+
### Phase 5 — New tests that don't have a home today
|
|
327
|
+
|
|
328
|
+
Things we want to lock down but that don't fit any current spec:
|
|
329
|
+
|
|
330
|
+
- `extra-panels.spec.ts` — passing `extraPanels={["foo"]}` on
|
|
331
|
+
ChatCenteredShell allows `surface.openPanel({component:"foo"})`; omitting it
|
|
332
|
+
blocks. Currently silent.
|
|
333
|
+
- `multi-shell.spec.ts` — two ChatCenteredShells on the same page with distinct
|
|
334
|
+
`storageKey` props don't trample each other's persisted state. Smoke-level
|
|
335
|
+
guard for future multi-pane embeds.
|
|
336
|
+
- `keyboard-shortcuts.spec.ts` — Cmd+1, Cmd+2, Esc behaviour. Touchy in
|
|
337
|
+
headless; documented separately so it can be skipped on CI matrices that
|
|
338
|
+
have known-flaky keyboards.
|
|
339
|
+
|
|
340
|
+
Phase 5 is opportunistic — none of it blocks Phases 1–4.
|
|
341
|
+
|
|
342
|
+
## Enforcement / guardrails
|
|
343
|
+
|
|
344
|
+
- Add a lightweight ownership checklist to PR template:
|
|
345
|
+
- "Is this reusable across >=2 apps?" → package.
|
|
346
|
+
- "Is this product policy/brand/domain?" → app.
|
|
347
|
+
- Add CI boundary lint/audit tasks (initially warning, later fail):
|
|
348
|
+
- detect app-specific domain modules imported into package runtime code
|
|
349
|
+
- detect duplicated mock-api implementations when package testing helpers exist
|
|
350
|
+
- Keep `apps/workspace-playground` success metric: tiny, readable demo codebase.
|
|
351
|
+
|
|
352
|
+
## Non-goals
|
|
353
|
+
|
|
354
|
+
- **Not** moving `boring-macro-v2`'s 14 macro-only specs upstream. They depend
|
|
355
|
+
on the macro Fastify routes + ClickHouse + the macro app's panes. Putting
|
|
356
|
+
them in workspace would force workspace to either mock those (unfaithful) or
|
|
357
|
+
spawn macro (heavy + wrong scope).
|
|
358
|
+
- **Not** generating a "second playground" for tests. The fixture app is a
|
|
359
|
+
test artifact, deliberately ugly and stable; the playground is a
|
|
360
|
+
documentation artifact, deliberately readable and idiomatic.
|
|
361
|
+
- **Not** unifying with `boring-ui-v2/e2e/`'s spawnBackend fixtures yet. That
|
|
362
|
+
suite is mature for backend testing; bolting browser specs onto it adds risk
|
|
363
|
+
without removing duplication. Revisit only if the helpers genuinely converge.
|
|
364
|
+
|
|
365
|
+
## Deliverables
|
|
366
|
+
|
|
367
|
+
1. `packages/workspace/e2e/fixture/**` + migrated generic specs.
|
|
368
|
+
2. Reduced `apps/boring-macro-v2/e2e/**` containing only macro-specific specs.
|
|
369
|
+
3. Playground mock backend deduplicated with workspace testing utility.
|
|
370
|
+
4. Follow-up extraction beads:
|
|
371
|
+
- core server helpers from full-app
|
|
372
|
+
- delete macro `tabBus`; route via existing UI bridge
|
|
373
|
+
- generic queue/rate-limit primitive from macro refresh flow
|
|
374
|
+
5. Boundary lint/checklist documented and wired into CI.
|
|
375
|
+
|
|
376
|
+
## Open questions
|
|
377
|
+
|
|
378
|
+
- Does the fixture need its own `@boring/agent` backend (for the bridge specs),
|
|
379
|
+
or can the bridge run in-process inside the Vite plugin? Probably the
|
|
380
|
+
former, mirroring how the playground already boots `createAgentApp` from
|
|
381
|
+
vite.config.ts.
|
|
382
|
+
- Should the fixture export its mock adapter from `@boring/workspace/testing`
|
|
383
|
+
so child apps can reuse it? Tempting. Risk is that "testing" subpath grows
|
|
384
|
+
into another public surface to maintain. Decide after the fixture stabilises.
|
|
385
|
+
- CI cost: the fixture suite + macro suite + root e2e all boot Vite. Total
|
|
386
|
+
cold-boot is ~3 minutes of Vite work. If that's too much, share a
|
|
387
|
+
`vite-node-server` process across suites. Out of scope for the initial move.
|