# A3 Project Conventions A3 workflow project using `@athree/runner` for LLM-powered browser automation. ## Commands ```bash npm run dev # TypeScript watch mode npm run compile # Build to out/ npm run lint # ESLint with @nodescript/eslint-config ``` ## Project Structure - `src/workflows/` - Workflow classes with `.workflow.ts` suffix - `src/schema/` - Zod schemas for inputs (Dataset) and outputs (Result) - `datasets/` - YAML files defining work units - `services/` - Output directory (gitignored) ## TypeScript - ES modules with `.js` extension in imports: `import { X } from './file.js'` - Target ES2024, strict mode enabled - Decorators enabled (`@workflow`, `@dep()`) - Never use `any` - use `unknown` with type guards or Zod validation - No type casting with `as` keyword ## Dependency Injection (mesh-ioc) ```typescript @dep() private service!: ServiceClass; ``` - Keep `@dep()` fields private - Never use `new` with DI classes - Use `init()` method instead of constructor logic if initialisation needed ## Coding Style - OOP over FP - `async/await` over raw promises - `for...of` instead of `forEach` - Guard clauses (early returns) over nested if/else - No underscore prefix for private fields - Compact method bodies, no unnecessary blank lines - British English in comments and docs ## Workflow Pattern ```typescript import { ChatService, BrowserService, workflow } from '@athree/runner'; import { dep } from 'mesh-ioc'; @workflow({ title: 'My Workflow' }) export class MyWorkflow { @dep() private chat!: ChatService; @dep() private browserService!: BrowserService; async run() { const page = await this.browserService.getPage(); // workflow logic } } ``` ## Zod Schemas ```typescript import { z } from 'zod'; export const DatasetSchema = z.object({ serviceId: z.string().describe('Unique identifier'), url: z.string().url().describe('URL to process'), }); export type Dataset = z.infer; ```