File size: 13,240 Bytes
d1bff6b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# Coding Guidelines

These guidelines are the default standard for this repository. Source code, comments, identifiers, documentation inside the codebase, and commit-facing technical language should be written in English.

The goal is boring, maintainable software: small files, clear boundaries, explicit names, predictable control flow, and changes that are easy to review.

---

## Core Principles

- Prefer clarity over cleverness. A straightforward solution that everyone can maintain beats a compact abstraction.
- Keep changes small and goal-driven. Every changed line should map to the current task.
- Optimize for reading. Code is read more often than it is written.
- Apply KISS first, DRY second. Duplication is acceptable until the shared concept is real and stable.
- Follow SOLID pragmatically. Do not introduce interfaces or classes just to satisfy a pattern.
- Use early returns and guard clauses to keep the happy path shallow.
- Avoid speculative configuration, compatibility layers, or extension points unless there is a current consumer.
- Do not mix orchestration, IO, parsing, domain decisions, and UI rendering in the same function or component.

---

## Architecture Boundaries

### Electron Desktop

- Main process code owns OS, filesystem, child processes, IPC registration, native dialogs, and app lifecycle.
- Renderer code owns UI, local UI state, visual composition, and user interactions.
- Preload code is the only bridge. Keep exposed APIs narrow and typed.
- IPC handlers should be thin. Move command resolution, env parsing, process management, and report detection into focused modules.
- Avoid long `*.ipc.ts` files. Split by capability, for example `test-runner`, `local-apps`, `agent-session`, `reports`, `permissions`.
- Never let renderer components know command-line construction details. Renderer passes intent; main process resolves execution.

### Agent Runner

- Provider adapters should translate between Specwright contracts and provider APIs only.
- Shared generation flow belongs outside individual providers.
- Keep transport concerns separate from protocol concerns. HTTP, SSE, CLI, and ACP should not be interleaved in one long function.
- Provider selection and environment parsing belong in the registry/config layer, not inside generation logic.

### Plugin Runtime

- `stepHelpers.js` remains the single source of truth for `FIELD_TYPES`.
- `testDataGenerator.js` remains the single source of truth for generated test data behavior.
- Step files import `Given`, `When`, `Then`, `Before`, `After`, and `expect` from `fixtures.js` only.
- Generated tests should prefer stable user-observable behavior over implementation details.
- Fixture changes must preserve normal Playwright behavior first. Special modes, such as CDP, must be explicit opt-in.

---

## File Size And Module Shape

- Target files under 250 lines.
- Treat files over 400 lines as refactor candidates.
- Treat files over 800 lines as architectural debt that needs an extraction plan.
- A file should have one primary reason to change.
- Extract by responsibility, not by syntax. Do not create a `utils.ts` dumping ground.
- Prefer feature folders with local helpers near their consumer.
- Keep public APIs small. Export only what another module actually uses.
- Avoid barrels when they hide dependency direction or create circular imports.

Good extraction targets:

- Pure parsing and normalization functions.
- Process spawning and lifecycle management.
- External API clients.
- UI subcomponents with independent state or layout.
- State selectors and derived view models.
- Reusable test fixtures or environment adapters.

Bad extraction targets:

- Single-use wrappers with vague names.
- Abstract base classes without multiple stable implementations.
- Generic helpers named `common`, `misc`, `utils`, or `helpers` without a domain noun.

---

## Naming

- Use English names everywhere.
- Use descriptive names. Avoid one-letter variables except conventional local coordinates or indexes in tiny scopes.
- File names should describe the thing they contain.
- Avoid suffixes unless they add real information. Keep `.spec` for tests.

```text
zone-editor.ts       not zone-editor.component.ts
fonts.ts             not fonts.service.ts, if it is a helper rather than an injectable service
```

- Event handlers start with `on`.

```ts
// Bad
(click)="zoneSelect.emit(zone)"

// Good
(click)="onZoneClick(zone)"
```

- Injected service properties must describe the service.

```ts
// Bad
private readonly storage = inject(SettingsService);

// Good
private readonly settingsService = inject(SettingsService);
```

- Injection tokens are `UPPER_SNAKE_CASE`.

```ts
export const ZONE_COMPONENTS = new InjectionToken<ZoneComponent[]>('Zone components');
```

- Library components use the `mpo` prefix. CSS classes use dashed-case.
- Prefer domain names over technical names: `resolveRunCommand` is better than `processInput`.
- Boolean names should read as predicates: `isRunning`, `hasAuth`, `canRetry`, `shouldStream`.

---

## TypeScript

- Prefer explicit return types for exported functions, public methods, IPC handlers, and async functions.
- Avoid `any`. If the shape is unknown, use `unknown` and narrow it.
- Avoid type assertions. Prefer type guards, schema validation, or typed APIs.
- Use `undefined` instead of `null` unless an external API requires `null`.
- Always use braces for control flow.
- Put `else` on a new line when it is needed. Prefer early return over `else` after a guard.
- Prefer `for...of` over `forEach`, especially for async code and better stack traces.
- Store repeated expressions in a named `const`.
- Use shorthand object notation.
- Keep filter predicates inline when they are trivial. Extract only if the predicate has a domain name and is reused.
- Use discriminated unions for state machines instead of loosely related booleans.
- Use `Record<string, unknown>` carefully. Prefer named types for cross-module contracts.

```ts
// Bad
function run(input: any) {
  if (input.enabled) {
    doWork(input.value);
  }
}

// Good
function run(input: RunInput): void {
  if (!input.enabled) {
    return;
  }

  doWork(input.value);
}
```

---

## Functions

- A function should do one thing at one level of abstraction.
- Prefer functions under 40 lines. Longer functions need a reason.
- Use guard clauses for invalid, empty, or unsupported cases.
- Keep the main path left-aligned.
- Avoid boolean parameters when they change behavior significantly. Use an options object or split the function.
- Avoid hidden side effects in functions that look like queries.
- Do not catch errors only to rethrow the same error.
- When catching errors, add context or recover intentionally.
- Name functions after the outcome, not the implementation detail.

```ts
// Bad
function handle(data: RawIssue): Issue {
  if (data.fields) {
    if (data.fields.title) {
      return mapIssue(data);
    }
  }
  throw new Error('Invalid issue');
}

// Good
function parseIssue(data: RawIssue): Issue {
  if (!data.fields?.title) {
    throw new Error('Issue title is required');
  }

  return mapIssue(data);
}
```

---

## Classes And Services

- Prefer functions and small modules until state or lifecycle makes a class useful.
- Classes should have one responsibility.
- Keep constructor work minimal. Start IO explicitly.
- Class members are ordered: `public` then `protected` then `private`.
- Keep inputs and outputs grouped.
- Methods follow the same order: public, protected, private.

```ts
export class MyComponent {
  public readonly type = input<string>();
  public readonly valueChanged = output<string>();

  protected readonly zones = this.zoneService.zones;

  private readonly settingsService = inject(SettingsService);

  public save(): void {
    // ...
  }

  protected onZoneClick(zone: Zone): void {
    // ...
  }

  private buildPayload(): Payload {
    // ...
  }
}
```

---

## React And Renderer Code

- Components should primarily render. Move command resolution, parsing, persistence, and IO into hooks or services.
- Split components when separate parts have separate state, effects, or responsibilities.
- Avoid `useMemo` and `useCallback` by default. Use them only for expensive work, stable dependencies required by a child, or existing project patterns.
- Effects synchronize with external systems. Do not use effects to derive state that can be computed during render.
- Prefer custom hooks for reusable UI behavior, not for hiding unrelated complexity.
- Zustand stores should expose focused actions and selectors. Avoid one store becoming an application service locator.
- Keep renderer state serializable where possible.
- Do not construct shell commands in React components.

---

## Angular, Signals, And Resources

- Do not use `this` in templates.
- Do not call getters or methods from templates. Use signals or computed values.
- A signal that never changes is a constant.
- Do not wrap signals in `params` unnecessarily. Pass the signal reference when possible.
- Store signal output in a local variable when it is read more than once.
- Always check `hasValue()` before `value()` in reactive contexts.
- Use `equal: compareObjects` for object-valued signals where supported.
- Expose `FormControl` values as signals instead of duplicating writable state.
- Use `rxResource` with `timer` for periodic fetching.
- Use `OnDestroy` when lifecycle cleanup is required.
- Use `HostListener` when component inputs need to be checked.
- Services should be provided at the narrowest useful scope.
- If a file creates no injectable state and only transforms data, it is a helper, not a service.

---

## Templates

- Keep templates declarative.
- Move multi-statement template logic into an event handler.
- HTML attributes go on new lines when the element becomes hard to scan.
- Attribute order: `class`, `[style]`, `[class]`, bindings, events.
- Use two spaces for indentation. Do not use tabs.
- Put `@else` on a new line.
- Use self-closing tags with a space before the slash.

```html
<mpo-carousel />
```

---

## CSS And SCSS

- Prefer design tokens and theme variables over literal colors.
- Keep CSS close to the component or feature when possible.
- Large global stylesheets must be split by layer: tokens, base, layout, components, utilities.
- Avoid unused styles. Delete styles when deleting the UI that uses them.
- Avoid selector chains that depend on deep DOM structure.
- Do not use `::ng-deep`. Use `:host`, component APIs, or explicit CSS variables.
- CSS class names are dashed-case and domain-specific.

---

## Error Handling And Logging

- Error messages should say what failed and include the relevant context.
- Do not swallow errors silently unless the operation is truly optional.
- Logs should be actionable. Avoid noisy logs in hot paths.
- Main-process logs should include the subsystem prefix.
- Do not log secrets, tokens, full auth payloads, or private user content.
- Prefer typed result objects for expected failures and exceptions for unexpected failures.

---

## Testing

- Tests should prove behavior, not implementation details.
- Add or update tests when changing command resolution, process management, provider behavior, fixtures, or generated-output contracts.
- Playwright assertions should prefer user-visible results and stable accessibility selectors.
- Avoid fallback assertions that can pass without proving the scenario.
- When adding a bug fix, reproduce the bug first where feasible.
- Keep test helpers small and named after user intent.

---

## Comments And Documentation

- Comments must be in English.
- Explain why, not what the next line already says.
- Prefer deleting misleading comments over updating around them.
- Public architecture decisions should live in `docs/`, not as long comments in code.
- If code needs a long comment to be understandable, consider extracting names or functions first.

---

## Review Checklist

Before opening or merging a change, check:

- Is the main path easy to read without jumping across many files?
- Did the change stay inside the right architectural boundary?
- Did any file exceed 400 lines or become noticeably more mixed in responsibility?
- Are names specific enough to understand intent without comments?
- Are invalid states impossible or guarded early?
- Are errors actionable and free of secrets?
- Are tests or verification commands included for behavior changes?
- Did you remove unused imports, types, functions, files, and CSS?
- Did you avoid adding abstractions with only one speculative consumer?

---

## Repository-Specific Rules

- Node must satisfy the repository engine: `>=20.20.2`.
- Use `pnpm` from the workspace root for installs and workspace commands.
- Do not run package installs from nested application folders.
- Keep `.gitignore` in the project root unless a nested repository explicitly requires its own.
- Generated or scaffolded plugin behavior must preserve existing consumer projects unless a migration is explicit.
- Desktop builds require `@specwright/agent-runner` to be built when packaging or testing packaged flows.
- `agent-runner` is CommonJS output; MCP server is ESM/plain JS. Do not blur module-system assumptions.