- Never run a "dev" or "build" command, chances are I'm already running it in the background. Just ask me to check for updates or whatever you need
- Never ever ever write a "TO DO" comment. If you've been told to do something, DO IT. Don't stop halfway. Never give up and just leave a "to do" comment and say - "haha heres working code :)" - that is unacceptible. Always finish your task, no matter how many iterations you need to perform.
- DO NOT alter .gitignore
- JS Doc comments should be SHORT and SWEET. Don't need examples unless ABSOLUTELY necessary
- When using db schemas in Drizzle, import them from '@autumn/shared', and don't do schemas.
- When writing tests, ALWAYS read:
server/tests/_guides/general-test-guide.md- Common patterns, client initialization, public keys- Case-specific guide (e.g.,
server/tests/_guides/check-endpoint-tests.mdfor/checktests)
- When running tests, ALL server-side console logs go to the server's logs which you do not have access to. You must ask the user to paste you in the logs, instead of expecting the server logs to magically appear in the test logs. Use your common sense
-
You can access the biome linter by running
bunx biome check <folder or file path>. Always specify a folder path, as the codebase is quite large and you will get out of scope errors that you are not burdened to correct. If you would like to let biome automatically fix as much as it can, usebunx biome check --write <folder or file path> -
Note, biome does not perform typechecking. In which case you need to, you may run
tsgo --noEmit --skipLibCheck <folder or file path> -
The
server/src/_luaScriptsV2/folder contains Lua scripts for Redis atomic operations. Redis uses Lua 5.1 - there is NOgotostatement (added in Lua 5.2), so use if/else blocks instead. -
This codebase uses Bun as its preferred package manager and Node runtime.
-
ALWAYS import from
zod/v4, not fromzoddirectly. Example:import { z } from "zod/v4"; -
Always prefer foo({ bar }) over foo(bar) method signatures - no matter if we are using only one argument or not, object as param are always better, as in the future when wanting to change the order of parameters, or add new ones - its easier.
-
When creating "hooks" folders, don't nest them under "components"
-
Functions (unless there's a very good reason) should always take in objects as arguments. Object params are named and easy to understand.
-
For regular functions, use inline object types in the function signature rather than creating separate type definitions. Only create named types when they're reused across multiple functions or exported.
// ❌ BAD - Unnecessary type definition for single-use params type DoSomethingParams = { ctx: AutumnContext; customerId: string; }; const doSomething = async ({ ctx, customerId }: DoSomethingParams) => { ... } // ✅ GOOD - Inline object type const doSomething = async ({ ctx, customerId }: { ctx: AutumnContext; customerId: string }) => { ... }
-
This codebase uses Bun for all of its operations in
/server,/viteand/shared. It uses Bun for the package management, Bun for the workspace management and Bun for the runtime. Prefer Bun over PNPM. If you ever want to trace a package dependency tree, runbun why <package name>which will tell you why a certain package was installed and by who. -
Prefer Guard clauses "if(!admin) return;" over explicity "if(admin) do X;" Early returns are better
-
Do not run "npx tsc" - run "tsc" instead as it is installed globally.
-
ALWAYS use
.meta()for zod-openapi schema registration, NOT.openapi(). Example:ApiProductSchema.meta({ id: "Product" }) -
ALWAYS use
c.req.param()to get route parameters in Hono handlers, NOTc.req.valid("param"). Example:const { customer_id } = c.req.param(); -
When referring to a
customer_entitlementobject (or pluralcustomer_entitlements), always use the full name. Do not abbreviate to "entitlement" or "entitlements" as this will be confused with the separateentitlementobject.
- NEVER use
c.json({ message: "...", code: "..." }, statusCode)pattern for input validation or expected errors in Hono routes - ALWAYS throw
RecaseErrorfrom@autumn/sharedfor all validation errors, not found errors, forbidden errors, etc. - For internal/unexpected errors (like missing configuration, database errors, etc.), throw
InternalErrorfrom@autumn/shared - The onError middleware automatically converts these errors to appropriate HTTP responses
- Examples:
// ❌ BAD - Don't do this if (!org) { return c.json({ message: "Org not found", code: "not_found" }, 404); } // ✅ GOOD - Validation/expected errors use RecaseError if (!org) { throw new RecaseError({ message: "Org not found", code: ErrCode.NotFound, statusCode: 404, }); } // ✅ GOOD - Internal/unexpected errors use InternalError if (!upstash) { throw new InternalError({ message: "Upstash not configured", code: "upstash_not_configured", }); }
/ root -> components |-> hooks
/ root -> components -> hooks
-
Functions (unless there's a very good reason) should always take in objects as arguments. Object params are named and easy to understand.
-
This codebase uses Bun for all of its operations in
/server,/viteand/shared. It uses Bun for the package management, Bun for the workspace management and Bun for the runtime. Prefer Bun over PNPM. If you ever want to trace a package dependency tree, runbun why <package name>which will tell you why a certain package was installed and by who. -
Prefer Guard clauses "if(!admin) return;" over explicity "if(admin) do X;" Early returns are better
-
Do not run "npx tsc" - run "tsc" instead as it is installed globally.
- When you are using the Figma MCP server, you must follow our design system. Below is an example implementation of CVA with out design system
DON'T name files one word (like index.ts, model.ts, etc.). Give proper indication in the filename to which resource it's targeting. For example, a utility file for organizations should be named orgUtils.ts. This is because it's easier to search for files like this. That being said, the filename shouldn't be overly long (less than three words is ideal)
- Always use v2 components from
@/components/v2/(buttons, inputs, dialogs, sheets, selects, etc.) for new features. Old components in@/components/ui/are deprecated.
- Use
Sheet.tsxfor overlay sheets (modal-style with backdrop). UseSheetHeader,SheetFooter,SheetSectionfromSharedSheetComponents.tsxfor consistent styling. InlineSheet.tsxprovidesSheetContainerfor inline sheets (embedded in page layout). It re-exports shared components for backwards compatibility.- Both sheet types support the same header/footer/section components, ensuring consistent UI patterns across overlay and inline implementations.
- DO NOT hardcode styles when possible. Always try to reuse existing Tailwind classes or component patterns from similar components in the codebase.
- When adding interactive elements (hover, focus, active states), look for existing patterns in similar components and reuse those class combinations.
- Consistency is key - if a pattern exists, use it rather than creating a new one.
- When creating form input elements (inputs, selects, textareas, etc.) in the vite folder, ALWAYS read
vite/FORM_DESIGN_GUIDELINES.mdfirst to understand the atomic CSS class system.