This document contains mandatory rules for AI coding assistants working on the Epoch Flow project. Read this entire file before making any changes.
- NEVER use single-character variable names.
- Exception: Loop indices (
i,j,k) and coordinate pairs (x,y) only. - Bad:
const n = getName()— Good:const userName = getName() - Bad:
const d = new Date()— Good:const currentDate = new Date() - Bad:
const e = event— Good:const clickEvent = event
- NEVER use the
anytype. Ever. - Use
unknownwhen the type is genuinely unknown at compile time. - Use proper generics (
T,K,V) with meaningful constraints. - Use discriminated unions or branded types when needed.
- Bad:
function parse(data: any)— Good:function parse<T>(data: unknown): T - If you encounter
anyin existing code, refactor it as part of your change.
- NEVER use the
askeyword for type assertions. - Never:
value as string,element as HTMLInputElement,data as MyType - Use type guards (
typeof,instanceof, custom predicates) instead. - Use proper type narrowing with
ifstatements. - Fix the underlying type issue rather than asserting around it.
- Bad:
const input = event.target as HTMLInputElement - Good:
if (event.target instanceof HTMLInputElement) { const input = event.target }
- All packages use
strict: trueintsconfig.json. - Enable
noImplicitReturns,noFallthroughCasesInSwitch,noUnusedLocals,noUnusedParameters. - Explicit return types on all exported functions and public class methods.
- Prefer
interfaceovertypefor object shapes. Usetypefor unions, mapped types, and utility types.
- Write code that is self-documenting. Prefer clear variable names over comments explaining what a variable does.
- Keep functions small and focused (< 20 lines when possible).
- Avoid nested ternaries. Use early returns.
- Prefer
constoverlet. Never usevar.
- ALWAYS update files in the
docs/directory when implementing or changing features. - Mark tasks as complete (
- [x]) in the relevant day document as you finish them. - If a task doesn't exist in the docs, add it before implementing.
- When you change the public API, update:
- The relevant
docs/day-X-*.mdfile spike.mdhigh-level checkboxesdocs/README.mdstatus table if phases complete
- The relevant
- When adding new concepts, add them to the appropriate architecture section in docs.
- All exported functions must have TSDoc comments with
@paramand@returns. - Complex internal functions should have inline comments explaining the "why", not the "what".
- Keep
README.mdaccurate if it references specific APIs or examples.
- EVERY new function, utility, or logic change must include unit tests.
- Use Vitest for unit tests in
packages/core/andpackages/react/. - Tests must cover:
- Happy path
- Error cases
- Edge cases (empty input, null/undefined, boundary values)
- Test files live next to source files:
foo.ts→foo.test.ts. - Aim for descriptive test names:
it('should return null when schema validation fails').
- ALL user-facing flows must have Playwright E2E tests.
- E2E tests live in
apps/demo/e2e/. - Test critical user journeys:
- Full form submission flow
- Step navigation (next, back, go-to)
- Draft persistence (refresh page, data returns)
- Validation errors per step
- Mobile responsiveness (viewport testing)
- Use
data-testidattributes for selectors, never rely on CSS classes or text content.
- Run
pnpm testbefore declaring any task complete. - Fix failing tests immediately. Do not skip or ignore failing tests.
- If you add a new test framework (like Playwright), ensure CI runs it.
packages/core/is framework-agnostic. No React, no DOM APIs.packages/react/is the only React-aware package. It depends oncore.apps/demo/is the only app. It depends onreact.- Never create circular dependencies between packages.
- Epoch Flow is headless. Never add UI components, CSS, or styling to
packages/core/orpackages/react/. - The demo app (
apps/demo/) is the only place for UI and styling.
zod,react,react-dom,react-hook-form,@trpc/*are peer dependencies.- Never bundle peer dependencies. Mark them as
externalin Vite configs.
- Read the relevant
docs/day-X-*.mdfile. - Understand the current state by reading existing code.
- Update the todo list in your response.
- Make the smallest change that achieves the goal.
- Run tests frequently (
pnpm test --runfor speed). - Run typecheck (
pnpm typecheck) before finishing.
- Verify all tests pass:
pnpm test - Verify types pass:
pnpm typecheck - Verify build passes:
pnpm build - Update documentation in
docs/andspike.md. - Review your diff for:
- No
anytypes - No
asassertions - No single-character variables
- No missing tests
- No
- ❌
const x = ...— use descriptive names - ❌
function foo(a: any, b: any)— use proper types - ❌
const value = something as string— use type guards - ❌
letwhenconstwould work - ❌ Changing code without updating
docs/ - ❌ Submitting code without tests
- ❌ Adding UI components to
packages/core/orpackages/react/ - ❌ Using
@ts-ignoreor@ts-expect-error
// ✅ Good: Descriptive names, no any, no as, proper types
interface UserProfile {
displayName: string
emailAddress: string
accountCreatedAt: Date
}
function formatUserProfile(profile: UserProfile): string {
const formattedDate = profile.accountCreatedAt.toLocaleDateString()
return `${profile.displayName} (${profile.emailAddress}) — ${formattedDate}`
}
// ✅ Good: Type guard instead of as
function isValidInputElement(target: EventTarget | null): target is HTMLInputElement {
return target instanceof HTMLInputElement
}
function handleInputEvent(event: Event): void {
if (isValidInputElement(event.target)) {
const inputValue = event.target.value
processInputValue(inputValue)
}
}
// ✅ Good: Generics instead of any
function createStore<T>(initialValue: T): { get: () => T; set: (value: T) => void } {
let currentValue = initialValue
return {
get: () => currentValue,
set: (value: T) => { currentValue = value },
}
}Before completing any task, confirm:
- No single-character variables (except
i,j,k,x,yin appropriate contexts) - No
anytypes used - No
askeyword used - All functions have explicit return types
- Unit tests added and passing
- E2E tests added for user-facing changes
-
docs/updated with task progress -
spike.mdcheckboxes updated -
pnpm testpasses -
pnpm typecheckpasses -
pnpm buildpasses
These rules are mandatory. Do not bypass them for convenience.