CodeVetter · Repo Unpack · Contributor handoff
n8n-io/n8n
Use pnpm everywhere; build with turbo and redirect output (pnpm build > build.log 2>&1). All backend services are @n8n/di singletons resolved via Container.get; commands follow init/run/catch/finally; controllers/modules are decorator-registered. Respect hard ordering constraints (module entity registration before DataSource, PubSubRegistry re-init, no-async on WorkflowExecute.run, CSP middleware ordering). Test with per-package vitest; run lint+typecheck inside the touched package. TypeORM must stay inside @n8n/db or *.entity.ts/*.repository.ts files.
- Always use pnpm (npm install is blocked by a preinstall script); build via turbo ('pnpm build'), test via 'pnpm test' per package, lint via 'pnpm lint' (oxlint + eslint guardrails in cli), typecheck via 'pnpm typecheck'.
- Dependency injection: services are @Service()-decorated classes resolved with Container.get(X); decorators (@Command, @RestController, @OnPubSubEvent, @Memoized, @Env) from @n8n/decorators drive registration — new code should follow this pattern rather than manual wiring.
- Do not add 'async' to WorkflowExecute.run — it must return PCancelable for cancellation; this is an explicit in-code contract.
- DB access boundary (per AGENTS.md): business logic must not import @n8n/typeorm; queries live in repositories under @n8n/db or a module's database/ folder; transactions use the TransactionRunner port, not .manager.transaction.
- Encryption boundary (per AGENTS.md): new code encrypts only via cipher.encryptV2/decryptV2; raw AES classes stay in packages/core/src/encryption; deployment keys are deactivated, never deleted.
- Config changes go through @n8n/config config classes with @Env decorators — adding a setting means editing a *.config.ts (e.g. runners.config.ts), not reading process.env ad hoc; db type must be known before entities load (see bin/n8n comment).
- After adding module-level @OnPubSubEvent handlers or node loaders, remember the rewire step: PubSubRegistry.init() is re-invoked after initCommunityPackages and after module init in both start.ts and worker.ts.
- To run a single test file: cd into the package dir and 'pnpm test <file>'; cli integration tests need the vitest.config.integration.ts config and run on SQLite by default (DB_TYPE env switches dialect).
- Frontend work: editor-ui aliases '@/' to src; new feature modules belong under packages/modules/<name>/frontend (not editor-ui/src/features) and integrate via the manifest functions in main.ts; design-system components live in @n8n/design-system.
- Read first for most tasks: AGENTS.md (conventions), packages/cli/src/commands/base-command.ts + start.ts (init ordering), packages/cli/src/server.ts (middleware/controller wiring), packages/@n8n/backend-common/src/modules/module-registry.ts (module system), packages/core/src/execution-engine/workflow-execute.ts (execution engine).
- Trap: command name 'executeBatch' is aliased to 'execute-batch' at runtime; commands whose files fail to import surface a real error while missing files fall through to module-provided commands.
- V3 development: normal feature work lands on master behind opt-in flags; breaking changes only on the 3.x branch (per AGENTS.md reference to .github/DEVELOPING_V3.md).