@gears-frontx/react 0.1.0 → 0.2.0-alpha.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/CLAUDE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @gears-frontx/react
2
2
 
3
- React bindings and hooks for HAI3 applications. Provides the React integration layer with MFE (Microfrontend) support.
3
+ React bindings and hooks for FrontX applications. Provides the React integration layer with MFE (Microfrontend) support.
4
4
 
5
5
  ## React Layer
6
6
 
@@ -8,44 +8,146 @@ This package is part of the **React Layer (L3)** - it depends only on @gears-fro
8
8
 
9
9
  ## Core Concepts
10
10
 
11
- ### HAI3Provider
11
+ ### Gears FrontXProvider
12
12
 
13
- Wrap your app with HAI3Provider to enable all hooks:
13
+ Wrap your app with Gears FrontXProvider to enable all hooks:
14
14
 
15
15
  ```tsx
16
- import { HAI3Provider } from '@gears-frontx/react';
16
+ import { Gears FrontXProvider } from '@gears-frontx/react';
17
17
 
18
18
  function App() {
19
19
  return (
20
- <HAI3Provider>
20
+ <Gears FrontXProvider>
21
21
  <YourApp />
22
- </HAI3Provider>
22
+ </Gears FrontXProvider>
23
23
  );
24
24
  }
25
25
 
26
26
  // With configuration
27
- <HAI3Provider config={{ devMode: true }}>
27
+ <Gears FrontXProvider config={{ devMode: true }}>
28
28
  <YourApp />
29
- </HAI3Provider>
29
+ </Gears FrontXProvider>
30
30
 
31
- // With pre-built app
32
- const app = createHAI3().use(screensets()).build();
33
- <HAI3Provider app={app}>
31
+ // With pre-built app (host-style shell; host typically also uses queryCache() — see REACT.md)
32
+ const app = createGears FrontX().use(screensets()).build();
33
+ <Gears FrontXProvider app={app}>
34
34
  <YourApp />
35
- </HAI3Provider>
35
+ </Gears FrontXProvider>
36
+
37
+ // Child MFE app — canonical bootstrap matches src/mfe_packages/*/init.ts and .ai/targets/SCREENSETS.md:
38
+ // apiRegistry.register / initialize before .build; createGears FrontX().use(effects()).use(queryCacheShared()).use(mock()).build();
39
+ // registerSlice after .build when slices exist.
40
+ const mfeApp = createGears FrontX().use(effects()).use(queryCacheShared()).use(mock()).build();
41
+ <Gears FrontXProvider app={mfeApp}>
42
+ <YourApp />
43
+ </Gears FrontXProvider>
44
+ ```
45
+
46
+ The shared `QueryClient` is created and owned by the `queryCache()` framework plugin at L2.
47
+ `Gears FrontXProvider` resolves that client from the app instance — it does not create its own `QueryClient`.
48
+
49
+ When the host uses `queryCache()` and the child MFE app uses `queryCacheShared()` (with `effects()` and `mock()` on the same chain as in repo MFE inits), both roots join the same shared `QueryClient` while keeping separate React trees. `ThemeAwareReactLifecycle` relies on that shared plugin-owned client through the app instance. If the shared client is missing for a mounted MFE, the lifecycle now fails explicitly instead of silently falling back.
50
+
51
+ For separate roots, build each app with the appropriate query-cache plugin (`queryCache()` for the host, `queryCacheShared()` for child MFE shells) so every tree joins the same cache through plugin composition. MFE package ordering and forbidden bootstrap pieces are defined in SCREENSETS.md. Host apps should register domains/extensions during bootstrap; **`ExtensionDomainSlot`** is the preferred host-side renderer for screen slots while the framework wires `mount_ext`.
52
+
53
+ ### Data Fetching with Endpoint Descriptors
54
+
55
+ Services define endpoints as descriptors. Components consume them via `useApiQuery` and `useApiMutation`. No manual query keys, no `queryOptions()` calls.
56
+
57
+ ```tsx
58
+ import { useApiQuery, useApiMutation, apiRegistry } from '@gears-frontx/react';
59
+ import { AccountsApiService } from '../api/AccountsApiService';
60
+
61
+ function ProfileScreen() {
62
+ const service = apiRegistry.getService(AccountsApiService);
63
+
64
+ // Read — pass descriptor directly
65
+ const { data, isLoading, error } = useApiQuery(service.getCurrentUser);
66
+
67
+ // Read with params
68
+ const { data: user } = useApiQuery(service.getUser({ id: '123' }));
69
+
70
+ // Write with optimistic update
71
+ const { mutateAsync, isPending } = useApiMutation({
72
+ endpoint: service.updateProfile,
73
+ onMutate: async (variables, { queryCache }) => {
74
+ await queryCache.cancel(service.getCurrentUser);
75
+ const snapshot = queryCache.get(service.getCurrentUser);
76
+ queryCache.set(service.getCurrentUser, (old) => ({
77
+ ...old, user: { ...old.user, ...variables }
78
+ }));
79
+ return { snapshot };
80
+ },
81
+ onError: (_err, _vars, context, { queryCache }) => {
82
+ if (context?.snapshot) {
83
+ queryCache.set(service.getCurrentUser, context.snapshot);
84
+ }
85
+ },
86
+ onSettled: async (_data, _err, _vars, _ctx, { queryCache }) => {
87
+ await queryCache.invalidate(service.getCurrentUser);
88
+ },
89
+ });
90
+
91
+ // Per-endpoint cache override (rare)
92
+ const { data: config } = useApiQuery(service.getConfig, { staleTime: 0 });
93
+ }
94
+ ```
95
+
96
+ Cache keys are derived automatically by the service — `QueryCache` methods accept
97
+ endpoint descriptors directly (e.g., `queryCache.get(service.getCurrentUser)`).
98
+
99
+ ### SSE Streaming with Stream Descriptors
100
+
101
+ Services declare SSE endpoints as stream descriptors. Components consume them via `useApiStream`, which manages the EventSource lifecycle automatically (connect on mount, disconnect on unmount).
102
+
103
+ ```tsx
104
+ import { useApiStream, apiRegistry } from '@gears-frontx/react';
105
+ import { ChatApiService } from '../api/ChatApiService';
106
+
107
+ function ChatStream() {
108
+ const service = apiRegistry.getService(ChatApiService);
109
+
110
+ // Latest event only (default mode)
111
+ const { data, status, error } = useApiStream(service.messageStream);
112
+
113
+ // Accumulate all events
114
+ const { events, status: streamStatus } = useApiStream(
115
+ service.messageStream,
116
+ { mode: 'accumulate' }
117
+ );
118
+
119
+ // Deferred connection (enabled: false)
120
+ const [active, setActive] = useState(false);
121
+ const { data: msg, disconnect } = useApiStream(
122
+ service.messageStream,
123
+ { enabled: active }
124
+ );
125
+
126
+ if (status === 'connecting') return <Loading />;
127
+ if (error) return <Error error={error} />;
128
+
129
+ return <div>{data?.text}</div>;
130
+ }
36
131
  ```
37
132
 
133
+ `useApiStream` returns `{ data, events, status, error, disconnect }`:
134
+ - `data` — latest event payload (always set in both modes)
135
+ - `events` — all received events when `mode: 'accumulate'`; empty array in `'latest'` mode
136
+ - `status` — `'idle' | 'connecting' | 'connected' | 'disconnected' | 'error'`
137
+ - `error` — connection error if any
138
+ - `disconnect()` — manually close the connection
139
+
38
140
  ### Available Hooks
39
141
 
40
- #### useHAI3
142
+ #### useFrontX
41
143
 
42
- Access the HAI3 app instance:
144
+ Access the FrontX app instance:
43
145
 
44
146
  ```tsx
45
- import { useHAI3 } from '@gears-frontx/react';
147
+ import { useFrontX } from '@gears-frontx/react';
46
148
 
47
149
  function MyComponent() {
48
- const app = useHAI3();
150
+ const app = useFrontX();
49
151
 
50
152
  // Access MFE-enabled registry
51
153
  const extensions = app.screensetsRegistry.getRegisteredExtensions();
@@ -138,18 +240,18 @@ Access the MFE bridge for child MFEs:
138
240
 
139
241
  ```tsx
140
242
  import { useMfeBridge } from '@gears-frontx/react';
141
- import { HAI3_ACTION_LOAD_EXT, HAI3_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
243
+ import { Gears FrontX_ACTION_LOAD_EXT, Gears FrontX_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
142
244
 
143
245
  function MyExtension() {
144
246
  const bridge = useMfeBridge();
145
247
 
146
248
  // Execute actions chain on parent
147
249
  await bridge.executeActionsChain({
148
- action: { type: HAI3_ACTION_LOAD_EXT, target: 'screen', payload: { extensionId: 'other' } }
250
+ action: { type: Gears FrontX_ACTION_LOAD_EXT, target: 'screen', payload: { extensionId: 'other' } }
149
251
  });
150
252
 
151
253
  // Get shared property
152
- const theme = bridge.getProperty(HAI3_SHARED_PROPERTY_THEME);
254
+ const theme = bridge.getProperty(Gears FrontX_SHARED_PROPERTY_THEME);
153
255
  }
154
256
  ```
155
257
 
@@ -158,10 +260,10 @@ function MyExtension() {
158
260
  Subscribe to shared property changes:
159
261
 
160
262
  ```tsx
161
- import { useSharedProperty, HAI3_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
263
+ import { useSharedProperty, Gears FrontX_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
162
264
 
163
265
  function ThemedComponent() {
164
- const theme = useSharedProperty(HAI3_SHARED_PROPERTY_THEME);
266
+ const theme = useSharedProperty(Gears FrontX_SHARED_PROPERTY_THEME);
165
267
 
166
268
  return <div style={{ backgroundColor: theme?.primaryColor }}>...</div>;
167
269
  }
@@ -172,10 +274,10 @@ function ThemedComponent() {
172
274
  Invoke actions on the host application:
173
275
 
174
276
  ```tsx
175
- import { useHostAction, HAI3_ACTION_LOAD_EXT } from '@gears-frontx/react';
277
+ import { useHostAction, Gears FrontX_ACTION_LOAD_EXT } from '@gears-frontx/react';
176
278
 
177
279
  function MyExtension() {
178
- const loadExtension = useHostAction(HAI3_ACTION_LOAD_EXT);
280
+ const loadExtension = useHostAction(Gears FrontX_ACTION_LOAD_EXT);
179
281
 
180
282
  const handleClick = () => {
181
283
  loadExtension({ extensionId: 'other' });
@@ -254,7 +356,7 @@ import { MfeProvider } from '@gears-frontx/react';
254
356
 
255
357
  function MfeHost() {
256
358
  return (
257
- <MfeProvider bridge={parentBridge}>
359
+ <MfeProvider value={{ bridge: parentBridge, extensionId: 'home', domainId: 'screen' }}>
258
360
  <ExtensionContainer />
259
361
  </MfeProvider>
260
362
  );
@@ -270,17 +372,27 @@ import { ExtensionDomainSlot } from '@gears-frontx/react';
270
372
 
271
373
  function LayoutScreen() {
272
374
  return (
273
- <div>
274
- <ExtensionDomainSlot
275
- domainId="screen"
276
- containerRef={screenContainerRef}
277
- fallback={<Loading />}
278
- />
279
- </div>
375
+ <ExtensionDomainSlot
376
+ registry={registry}
377
+ domainId="screen"
378
+ extensionId="home"
379
+ loadingComponent={<Loading />}
380
+ />
280
381
  );
281
382
  }
282
383
  ```
283
384
 
385
+ `ExtensionDomainSlot` is the preferred host-side renderer for screen MFEs. It
386
+ owns mount/unmount and loading/error UI while host bootstrap handles domain
387
+ registration and shared property setup before slot-driven `mount_ext`.
388
+ The host `QueryClient` is reused by MFE React trees through
389
+ `queryCacheShared()`, not by embedding a `QueryClient` on `MfeMountContext`
390
+ or expecting host React context inside Shadow DOM.
391
+
392
+ When the domain's `ContainerProvider` must point at the same DOM node rendered
393
+ by the slot, pass `containerRef` to `ExtensionDomainSlot` and share that ref
394
+ with `RefContainerProvider`.
395
+
284
396
  #### RefContainerProvider
285
397
 
286
398
  Provide container references for MFE mounting:
@@ -302,11 +414,14 @@ function Layout() {
302
414
 
303
415
  ## Key Rules
304
416
 
305
- 1. **Wrap with HAI3Provider** - Required for all hooks to work
417
+ 1. **Wrap with Gears FrontXProvider** - Required for all hooks to work
306
418
  2. **Use hooks for state access** - Don't import selectors directly from @gears-frontx/framework
307
- 3. **Lazy load translations** - Use `useScreenTranslations` for screen-level i18n
308
- 4. **Use MFE hooks for extensions** - `useMfeBridge`, `useSharedProperty`, `useHostAction`, `useDomainExtensions`
309
- 5. **NO Layout components here** - Layout and UI components belong in L4 (user's project via CLI scaffolding)
419
+ 3. **Use endpoint descriptors for data** - `useApiQuery(service.endpoint)` for REST, `useApiStream(service.stream)` for SSE — not `queryOptions()` or manual key factories
420
+ 4. **Service is the cache contract** - The service IS the data layer; cache keys are derived automatically
421
+ 5. **QueryCache uses descriptors** - `queryCache.get(service.endpoint)`, not raw key arrays
422
+ 6. **Lazy load translations** - Use `useScreenTranslations` for screen-level i18n
423
+ 7. **Use MFE hooks for extensions** - `useMfeBridge`, `useSharedProperty`, `useHostAction`, `useDomainExtensions`
424
+ 8. **NO Layout components here** - Layout and UI components belong in L4 (user's project via CLI scaffolding)
310
425
 
311
426
  ## Re-exports
312
427
 
@@ -323,15 +438,19 @@ This allows users to import everything from `@gears-frontx/react` without needin
323
438
  ## Exports
324
439
 
325
440
  ### Components
326
- - `HAI3Provider` - Main context provider
441
+ - `Gears FrontXProvider` - Main context provider
327
442
  - `MfeProvider` - MFE context provider
328
443
  - `ExtensionDomainSlot` - Domain slot renderer
329
444
  - `RefContainerProvider` - Container reference provider
330
445
 
331
446
  ### Hooks
332
- - `useHAI3` - Access app instance
447
+ - `useFrontX` - Access app instance
333
448
  - `useAppDispatch` - Typed dispatch
334
449
  - `useAppSelector` - Typed selector
450
+ - `useApiQuery` - Declarative data fetch from endpoint descriptor; returns `ApiQueryResult<TData>`
451
+ - `useApiMutation` - Declarative mutation with endpoint descriptor and optimistic update support; returns `ApiMutationResult<TData>`
452
+ - `useApiStream` - Declarative SSE streaming from stream descriptor; returns `ApiStreamResult<TEvent>`
453
+ - `useQueryCache` - Restricted query cache access (accepts descriptors or raw keys)
335
454
  - `useTranslation` - Translation utilities
336
455
  - `useScreenTranslations` - Screen translation loading
337
456
  - `useTheme` - Theme utilities
@@ -343,14 +462,20 @@ This allows users to import everything from `@gears-frontx/react` without needin
343
462
  - `useActivePackage` - Subscribe to active GTS package
344
463
 
345
464
  ### Context
346
- - `HAI3Context` - React context (for advanced use)
465
+ - `Gears FrontXContext` - React context (for advanced use)
347
466
  - `MfeContext` - MFE context (for advanced use)
348
467
 
349
468
  ### Types
350
- - `HAI3ProviderProps`
469
+ - `Gears FrontXProviderProps`
470
+ - `ApiQueryResult<TData>` - Gears FrontX-owned query result type (data, error, isLoading, refetch, etc.)
471
+ - `ApiMutationResult<TData>` - Gears FrontX-owned mutation result type (mutateAsync, isPending, error, reset, etc.)
472
+ - `ApiStreamResult<TEvent>` - Gears FrontX-owned stream result type (data, events, status, error, disconnect)
473
+ - `ApiStreamOptions` - Stream hook options (mode, enabled)
474
+ - `QueryCache` - Restricted cache interface (accepts descriptors or raw keys)
475
+ - `MutationCallbackContext` - Context with queryCache injected into mutation callbacks
351
476
  - `MfeProviderProps`, `ExtensionDomainSlotProps`
352
477
  - `UseTranslationReturn`, `UseThemeReturn`
353
- - All types from @gears-frontx/framework
478
+ - All types from @gears-frontx/framework (including `EndpointDescriptor`, `MutationDescriptor`, `StreamDescriptor`, `StreamStatus`)
354
479
 
355
480
  ## Migration from Legacy API
356
481
 
@@ -378,10 +503,10 @@ function MyComponent() {
378
503
 
379
504
  **NEW**: Mount extension using app actions
380
505
  ```tsx
381
- import { useHAI3 } from '@gears-frontx/react';
506
+ import { useFrontX } from '@gears-frontx/react';
382
507
 
383
508
  function MyComponent() {
384
- const app = useHAI3();
509
+ const app = useFrontX();
385
510
  const containerRef = useRef<HTMLDivElement>(null);
386
511
 
387
512
  const handleNavigate = async () => {
@@ -403,16 +528,17 @@ function MyComponent() {
403
528
  }
404
529
  ```
405
530
 
406
- **NEW (Alternative)**: Use ExtensionDomainSlot
531
+ **NEW (Preferred for host screen slots)**: Use ExtensionDomainSlot
407
532
  ```tsx
408
533
  import { ExtensionDomainSlot } from '@gears-frontx/react';
409
534
 
410
535
  function MyComponent() {
411
536
  return (
412
537
  <ExtensionDomainSlot
538
+ registry={app.screensetsRegistry}
413
539
  domainId="screen"
414
- containerRef={screenContainerRef}
415
- fallback={<Loading />}
540
+ extensionId="home"
541
+ loadingComponent={<Loading />}
416
542
  />
417
543
  );
418
544
  }
@@ -1,5 +1,5 @@
1
1
  <!-- @standalone -->
2
- # hai3:duplicate-screenset - Duplicate Screenset
2
+ # frontx:duplicate-screenset - Duplicate Screenset
3
3
 
4
4
  ## AI WORKFLOW (REQUIRED)
5
5
  1) Read .ai/targets/SCREENSETS.md before starting.
@@ -18,7 +18,7 @@ Ask user for:
18
18
 
19
19
  ## STEP 1: Copy Screenset via CLI
20
20
  ```bash
21
- hai3 screenset copy SOURCE TARGET --category={category}
21
+ frontx screenset copy SOURCE TARGET --category={category}
22
22
  ```
23
23
  The CLI automatically:
24
24
  - Copies all files (screens, slices, actions, events, effects, API, icons, i18n).
@@ -50,7 +50,7 @@ STOP: If MCP connection is broken, fix it first. NEVER skip testing.
50
50
 
51
51
  ## CHECKLIST
52
52
  - [ ] Gather requirements (source, target name, category).
53
- - [ ] Run `hai3 screenset copy SOURCE TARGET --category={category}`.
53
+ - [ ] Run `frontx screenset copy SOURCE TARGET --category={category}`.
54
54
  - [ ] Run type-check (MUST pass).
55
55
  - [ ] Run arch:check (MUST pass).
56
56
  - [ ] Run lint (MUST pass).
@@ -1,10 +1,11 @@
1
1
  <!-- @standalone -->
2
- # hai3:new-component - Add New UI Component
2
+ # frontx:new-component - Add New UI Component
3
3
 
4
4
  ## AI WORKFLOW (REQUIRED)
5
- 1) Check for existing equivalent in project (e.g. components/ui/) and configured UI library first.
6
- 2) Gather requirements from user.
7
- 3) Implement.
5
+ 1) Read `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** any unit-test, validation, or testing steps in this command (project-owned overrides take precedence over defaults here).
6
+ 2) Check for existing equivalent in project (e.g. components/ui/) and configured UI library first.
7
+ 3) Gather requirements from user.
8
+ 4) Implement.
8
9
 
9
10
  ## CHECK EXISTING COMPONENTS FIRST
10
11
  - REQUIRED: Read `frontx.config.json` to find the `uikit` value.
@@ -38,11 +39,19 @@ File: src/components/ui/{ComponentName}.tsx (base primitives)
38
39
  Export from local index if needed.
39
40
 
40
41
  #### 1.3 Validation
41
- Run: npm run arch:check && npm run dev
42
+ <!-- @cpt-begin:cpt-frontx-flow-unit-test-generation-and-agent-verification-agent-verify:p1:inst-agent-implement-and-test -->
43
+ If creating a shared composite in `src/components/`, add or update a component test for its stable behavior.
44
+ Run `npm run test:unit`, `npm run type-check`, `npm run arch:check`, and `npm run dev` (or the equivalent commands for this project's configured package manager) and apply any stricter triggers or conventions from `.ai/project/targets/UNIT_TESTING.md`. Apply constraints from workflow step 1.
45
+ <!-- @cpt-end:cpt-frontx-flow-unit-test-generation-and-agent-verification-agent-verify:p1:inst-agent-implement-and-test -->
42
46
  Test component in UI.
43
47
 
48
+ ## TESTING
49
+ - REQUIRED: Load `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** interpreting unit-test requirements elsewhere in this command.
50
+ - REQUIRED: Follow `.ai/project/targets/UNIT_TESTING.md` for `__test-utils__/` placement and durable, non-placeholder assertions when adding or updating tests.
51
+
44
52
  ## RULES
45
53
  - REQUIRED: Check existing project UI components first; create new only if missing.
46
54
  - FORBIDDEN: Redux, business logic, side effects in components.
47
55
  - FORBIDDEN: Inline styles outside components/ui/.
48
56
  - REQUIRED: Accept value/onChange pattern for state.
57
+ - REQUIRED: New shared composite components in `src/components/` include a colocated or nearby unit test when the behavior is stable.
@@ -1,10 +1,10 @@
1
1
  <!-- @standalone -->
2
- # hai3:new-mfe - Create New Microfrontend Package
2
+ # frontx:new-mfe - Create New Microfrontend Package
3
3
 
4
4
  ## PREREQUISITES (CRITICAL - STOP IF FAILED)
5
5
 
6
6
  1. MFE must be associated with a screenset
7
- 2. Screenset must exist (run `hai3-new-screenset` first if needed)
7
+ 2. Screenset must exist (run `frontx-new-screenset` first if needed)
8
8
  3. Vite and Module Federation must be configured
9
9
 
10
10
  ## QUICK START
@@ -61,30 +61,17 @@ Create `src/lifecycle.tsx`:
61
61
  ```typescript
62
62
  import React from 'react';
63
63
  import type { ChildMfeBridge } from '@gears-frontx/react';
64
- import { ThemeAwareReactLifecycle } from '@gears-frontx/screensets/mfe/handler';
64
+ import { ThemeAwareReactLifecycle } from '@gears-frontx/react';
65
+ import { mfeApp } from './init';
65
66
  import { YourScreen } from './screens/YourScreen';
66
67
 
67
68
  class Lifecycle extends ThemeAwareReactLifecycle {
68
69
  constructor() {
69
- super({
70
- name: '{screensetName}',
71
- version: '1.0.0',
72
- });
70
+ super(mfeApp);
73
71
  }
74
72
 
75
- async mount(shadowRoot: ShadowRoot, bridge: ChildMfeBridge) {
76
- const div = document.createElement('div');
77
- shadowRoot.appendChild(div);
78
-
79
- const style = document.createElement('style');
80
- style.textContent = tailwindStyles;
81
- shadowRoot.appendChild(style);
82
-
83
- // Render component
84
- const root = ReactDOM.createRoot(div);
85
- root.render(<YourScreen bridge={bridge} />);
86
-
87
- return { root, div };
73
+ protected renderContent(bridge: ChildMfeBridge): React.ReactNode {
74
+ return <YourScreen bridge={bridge} />;
88
75
  }
89
76
  }
90
77
 
@@ -163,4 +150,4 @@ npm run dev:all
163
150
 
164
151
  See also:
165
152
  - `/packages/cli/template-sources/mfe-package/` for templates
166
- - `hai3-new-screenset.md` for screenset creation
153
+ - `frontx-new-screenset.md` for screenset creation
@@ -1,10 +1,11 @@
1
1
  <!-- @standalone -->
2
- # hai3:new-screen - Add New Screen
2
+ # frontx:new-screen - Add New Screen
3
3
 
4
4
  ## AI WORKFLOW (REQUIRED)
5
5
  1) Read .ai/targets/SCREENSETS.md and .ai/targets/EVENTS.md before starting.
6
- 2) Gather requirements from user (including UI sections).
7
- 3) Implement.
6
+ 2) Read `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** planning unit tests, validation, or any test-related tasks in this command (project-owned overrides take precedence over defaults here).
7
+ 3) Gather requirements from user (including UI sections).
8
+ 4) Implement.
8
9
 
9
10
  ## GATHER REQUIREMENTS
10
11
  Ask user for:
@@ -20,15 +21,20 @@ Ask user for:
20
21
  - Use actions to trigger state changes
21
22
  - FORBIDDEN: Direct slice dispatch from screen
22
23
  4) Add i18n with useScreenTranslations(). Export default.
23
- 5) Add to menu if requested.
24
- 6) Validate: `npm run type-check && npm run lint`.
25
- 7) Test via Chrome DevTools MCP (REQUIRED):
24
+ 5) Add or update a screen test that covers stable render or behavior for the new screen and place any test-only helpers in a sibling `__test-utils__/` folder.
25
+ 6) Add to menu if requested.
26
+ 7) Validate: run `test:unit`, `type-check`, and `arch:check` via this project's package manager per `.ai/project/targets/UNIT_TESTING.md` (which defines the exact commands). Apply constraints from workflow step 2.
27
+ 8) Test via Chrome DevTools MCP (REQUIRED):
26
28
  - Navigate to new screen
27
29
  - Verify screen renders without console errors
28
30
  - Test UI interactions and data flow
29
31
  - Verify translations load correctly
30
32
  - STOP if MCP connection fails
31
33
 
34
+ ## TESTING
35
+ - REQUIRED: Load `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** interpreting unit-test requirements elsewhere in this command.
36
+ - REQUIRED: Use neutral fixtures, translation mocks, and stable keys, roles, or IDs instead of placeholder-derived copy when adding or updating generated screen tests.
37
+
32
38
  ## COMPONENT PLAN
33
39
  - REQUIRED: Use local components (e.g. `components/ui/`) — no shared UI kit package.
34
40
  - components/ui/: base UI primitives (shadcn components or custom)
@@ -0,0 +1,57 @@
1
+ <!-- @standalone -->
2
+ # frontx:new-screenset - Create New Screenset
3
+
4
+ ## PREREQUISITES (CRITICAL - STOP IF FAILED)
5
+ Run `frontx --version`.
6
+ STOP: If fails, tell user to install.
7
+ FORBIDDEN: Creating screenset manually or by copying peers.
8
+
9
+ ## AI WORKFLOW (REQUIRED)
10
+ 1) Check prerequisites above.
11
+ 2) Read .ai/targets/SCREENSETS.md and .ai/targets/EVENTS.md before starting.
12
+ 3) Read `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** planning unit tests, validation, or any test-related tasks in this command (project-owned overrides take precedence over defaults here).
13
+ 4) Gather requirements from user (including UI sections).
14
+ 5) Implement.
15
+
16
+ ## GATHER REQUIREMENTS
17
+ Ask user for:
18
+ - Screenset name (camelCase).
19
+ - Category: Drafts | Mockups | Production.
20
+ - Initial screens.
21
+ - UI sections per screen (e.g., "stats cards, charts, activity feed").
22
+
23
+ ## STEP 1: Implementation
24
+ 1) Create screenset via `frontx screenset create` (REQUIRED).
25
+ 2) Create components BEFORE screen file per Component Plan.
26
+ 3) Implement data flow per .ai/targets/EVENTS.md:
27
+ - Actions emit events via eventBus.emit()
28
+ - Effects subscribe and update slices
29
+ - FORBIDDEN: Direct slice dispatch from components
30
+ 4) Add API service with mocks and endpoint descriptors:
31
+ - Read endpoints: `readonly prop = this.protocol(RestEndpointProtocol).query<T>('/path')`
32
+ - Write endpoints: `readonly prop = this.protocol(RestEndpointProtocol).mutation<T, V>('PUT', '/path')`
33
+ - NO queryOptions, NO manual query key factories outside the service
34
+ - Screens use `useApiQuery(service.prop)` and `useApiMutation({ endpoint: service.prop })`
35
+ 5) Add or update starter unit tests for stable generated behavior.
36
+ 6) Validate: run `test:unit`, `type-check`, `arch:check`, and `lint` via this project's package manager per `.ai/project/targets/UNIT_TESTING.md` (which defines the exact commands). Apply constraints from workflow step 3.
37
+ 7) Test via Chrome DevTools MCP (REQUIRED):
38
+ - Navigate to new screenset
39
+ - Verify screen renders without console errors
40
+ - Test user interactions trigger correct events
41
+ - Verify state updates via Redux DevTools
42
+ - STOP if MCP connection fails
43
+
44
+ ## COMPONENT PLAN
45
+ - REQUIRED: Use local components (e.g. `components/ui/`) — no shared UI kit package.
46
+ - components/ui/: base UI primitives (shadcn components or custom)
47
+ - components/: multi-screen shared components
48
+ - screens/{screen}/components/: screen-specific components
49
+
50
+ ## DATA FLOW
51
+ - Events: {domain events per EVENTS.md}
52
+ - State: slices/, events/, effects/, actions/
53
+ - API: api/{Name}ApiService.ts with mocks and endpoint descriptors
54
+
55
+ ## TESTING
56
+ - REQUIRED: Load `.ai/project/GUIDELINES.md` and `.ai/project/targets/UNIT_TESTING.md` when they exist **before** interpreting unit-test requirements elsewhere in this command.
57
+ - REQUIRED: Preserve the generated API-contract test and `HomeScreen` smoke test unless the new behavior replaces that baseline intentionally.