Spaces:
Sleeping
Sleeping
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.
|