Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
39cfc78
docs: add Phase 0 implementation design spec
Exc1D Apr 17, 2026
80d6ea4
chore: add .gitignore with .worktrees exclusion
Exc1D Apr 17, 2026
6ae3641
chore(foundation): initialize pnpm monorepo with Node 20
Exc1D Apr 17, 2026
2ee7b4e
chore(foundation): add Turborepo pipeline and strict TypeScript base …
Exc1D Apr 17, 2026
561f8cf
chore(foundation): add ESLint flat config and Prettier rules
Exc1D Apr 17, 2026
2e9ba04
chore(foundation): add editor and git hygiene configuration
Exc1D Apr 17, 2026
5eff7f9
chore(foundation): add Husky pre-commit hook with lint-staged
Exc1D Apr 17, 2026
596463d
chore(foundation): add Vitest workspace config and root README
Exc1D Apr 17, 2026
a7f12a6
feat(shared-types): scaffold types package with branded IDs and 5-rol…
Exc1D Apr 17, 2026
8b2b10b
fix(shared-types): remove stub types from public barrel, consolidate …
Exc1D Apr 17, 2026
e46d896
feat(shared-validators): implement canonicalPayloadHash per spec §6.2
Exc1D Apr 17, 2026
4f9e066
fix(shared-validators): guard Map/Set/RegExp to prevent silent hash c…
Exc1D Apr 17, 2026
6b4966a
feat(packages): scaffold shared-firebase, shared-ui, shared-sms-parse…
Exc1D Apr 17, 2026
5a52867
feat(citizen-pwa): scaffold Vite + React PWA with Hello screen
Exc1D Apr 17, 2026
6b3f80c
feat(responder-app): scaffold Vite + React + Capacitor config (Level …
Exc1D Apr 17, 2026
11eba07
feat(admin-desktop): scaffold Vite + React desktop PWA with Hello screen
Exc1D Apr 17, 2026
2069000
feat(functions): scaffold Cloud Functions v2 codebase (Node 20)
Exc1D Apr 17, 2026
0964fde
chore(firebase): add project config with hosting targets and emulator…
Exc1D Apr 17, 2026
87d81d2
feat(firestore): add default-deny rules with spec §5.7 helper stubs
Exc1D Apr 17, 2026
e6d64a2
feat(firebase): add default-deny rules for RTDB and Storage
Exc1D Apr 17, 2026
6f01b70
chore(terraform): add root config with provider pins and backend skel…
Exc1D Apr 17, 2026
a5d9654
feat(terraform): add firebase-project module enabling required GCP APIs
Exc1D Apr 17, 2026
44785a2
feat(terraform): add iam module with functions and ci-deploy service …
Exc1D Apr 17, 2026
5dc1794
feat(terraform): add secret-manager and pubsub modules
Exc1D Apr 17, 2026
14f38a4
feat(terraform): wire modules, add env configs, commit provider lock
Exc1D Apr 17, 2026
fd359e8
docs: update progress for Task 21
Exc1D Apr 17, 2026
0b283ea
ci: add GitHub Actions workflow with lint, typecheck, test, build, ru…
Exc1D Apr 17, 2026
86bf7b0
ci: add CodeQL, Dependabot, and CODEOWNERS
Exc1D Apr 17, 2026
13ad445
fix(verification): resolve vitest workspace, format docs, update prog…
Exc1D Apr 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 67 additions & 38 deletions .claude/rules/coding-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,50 @@ Based on Google TypeScript Style Guide and industry best practices.
## TypeScript/JavaScript

### File Basics

- Files use UTF-8 encoding
- Use `.ts` for TypeScript, `.tsx` for React TypeScript files
- One component per file (React)
- Use kebab-case or camelCase for file names (match project convention)

### Formatting

- 2 spaces for indentation (no tabs)
- Column limit: 80 characters (soft limit: 100 for JSX)
- Use `clang-format` or project formatter
- Trailing commas in multi-line literals

### Braces (K&R Style)

```ts
// No line break before opening brace
if (condition) {
doSomething();
doSomething()
}

// Empty blocks may be concise
if (condition) {} else {}
if (condition) {
} else {
}
```

### Whitespace

- One blank line between imports and implementation
- Single blank line between consecutive methods in classes
- No trailing whitespace
- Space after (not before) commas and semicolons
- Space before opening brace

### Variables

- Use `const` by default, `let` only when reassignment needed
- Never use `var`
- One variable per declaration
- Declare variables close to first use

### Naming

- Classes/Interfaces/Types/Enums: `UpperCamelCase`
- Functions/Methods/Variables: `lowerCamelCase`
- Constants: `UPPER_SNAKE_CASE`
Expand All @@ -50,6 +58,7 @@ if (condition) {} else {}
- File names: kebab-case (e.g., `user-profile.tsx`, `api-utils.ts`)

### Imports

- Use named exports, avoid default exports
- Use `import type` for type-only imports
- Group imports: external → internal → relative
Expand All @@ -59,21 +68,24 @@ if (condition) {} else {}
## TypeScript

### Type Safety

- Use strict TypeScript (`strict: true` in tsconfig)
- Never use `any` — use `unknown` instead
- Prefer `type` over `interface` for simple type aliases
- Use `interface` for object shapes that may be extended
- Use explicit return types for public functions

### Example

```typescript
// CORRECT: Explicit types
function processUser(user: User): Result<User> {
// ...
}

// AVOID: any
function processUser(user: any): any { // NEVER
function processUser(user: any): any {
// NEVER
// ...
}

Expand All @@ -84,23 +96,25 @@ async function parseResponse(response: unknown): Promise<ParsedData> {
```

### Type Assertion

```typescript
// CORRECT: Safe assertion with validation
const data = result as KnownType;
const data = result as KnownType

// CORRECT: Use unknown for API responses
async function fetchUser(id: string): Promise<unknown> {
const response = await fetch(`/api/users/${id}`);
return response.json();
const response = await fetch(`/api/users/${id}`)
return response.json()
}

// NEVER: Blind casting
const user = (response as any).data.user;
const user = (response as any).data.user
```

## React Components

### Component Structure

```typescript
// 1. Imports (external, then internal)
// 2. Types/Interfaces
Expand Down Expand Up @@ -129,18 +143,21 @@ export function UserProfile({ userId }: Props) {
```

### Hooks Rules

- Call hooks at top level only (no conditionals)
- Custom hooks must start with `use`
- Extract complex logic to custom hooks
- Keep hooks focused (split large hooks)

### State Management

- Prefer local state (`useState`) for component-specific state
- Use React Context for shared state
- Use Zustand/Redux for complex global state
- Co-locate state with its usage

### Performance

- Memoize expensive computations: `useMemo`
- Memoize callbacks passed to children: `useCallback`
- Use `React.lazy` for code splitting
Expand All @@ -159,93 +176,102 @@ const handleClick = useCallback(() => {
## Async/Await

### Rules

- Always handle errors in async functions
- Use `try/catch` for error handling
- Avoid async in useEffect (handle cleanup)
- Use `Promise.all` for parallel operations

### Example

```typescript
// CORRECT: Proper async handling
async function fetchData(): Promise<Data> {
try {
const response = await api.getData();
return response.data;
const response = await api.getData()
return response.data
} catch (error) {
console.error('Failed to fetch:', error);
throw new DataFetchError('Failed to fetch data');
console.error('Failed to fetch:', error)
throw new DataFetchError('Failed to fetch data')
}
}

// CORRECT: Parallel requests
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts(),
]);
const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()])

// CORRECT: Async useEffect with cleanup
useEffect(() => {
let cancelled = false;
let cancelled = false

async function load() {
const data = await fetchData();
if (!cancelled) setData(data);
const data = await fetchData()
if (!cancelled) setData(data)
}

load()

return () => {
cancelled = true
}

load();

return () => { cancelled = true; };
}, []);
}, [])
```

## Error Handling

### Rules

- Use typed errors where possible
- Never swallow errors silently
- Create custom error classes for domain errors
- Handle errors at component boundaries

### Custom Errors

```typescript
class ValidationError extends Error {
constructor(
message: string,
public field: string
public field: string,
) {
super(message);
this.name = 'ValidationError';
super(message)
this.name = 'ValidationError'
}
}

class NotFoundError extends Error {
constructor(resource: string, id: string) {
super(`${resource} with id ${id} not found`);
this.name = 'NotFoundError';
super(`${resource} with id ${id} not found`)
this.name = 'NotFoundError'
}
}
```

## CSS/Styling

### Rules

- Use CSS Modules, Tailwind, or styled-components (pick one)
- Follow existing project's styling approach
- Use CSS custom properties for theme values
- Avoid inline styles except for dynamic values
- Keep styles co-located with components

### Naming (CSS Modules)

```css
/* ComponentName.module.css */
.container { }
.headerTitle { }
.contentHidden { }
.container {
}
.headerTitle {
}
.contentHidden {
}
```

## Accessibility (a11y)

### Requirements

- All interactive elements must be keyboard accessible
- Use semantic HTML (`<button>`, `<nav>`, `<main>`)
- Provide alt text for images
Expand All @@ -254,6 +280,7 @@ class NotFoundError extends Error {
- Focus management for modals/dialogs

### Examples

```tsx
// CORRECT: Accessible button
<button onClick={handleSubmit} disabled={isLoading}>
Expand All @@ -268,8 +295,8 @@ class NotFoundError extends Error {
<input id="email" type="email" />

// CORRECT: Icon button accessibility
<button
aria-label="Close dialog"
<button
aria-label="Close dialog"
onClick={onClose}
>
<Icon name="close" />
Expand All @@ -279,6 +306,7 @@ class NotFoundError extends Error {
## File Organization

### Project Structure

```
src/
├── domains/ # Feature domains
Expand All @@ -297,12 +325,13 @@ src/
```

### Barrel Exports (index.ts)

```typescript
// Use barrel exports for public API
export { UserProfile } from './UserProfile';
export { useUser } from './hooks/useUser';
export type { User, UserRole } from './types';
export { UserProfile } from './UserProfile'
export { useUser } from './hooks/useUser'
export type { User, UserRole } from './types'

// AVOID: Re-export everything
export * from './everything';
export * from './everything'
```
Loading
Loading