Thank you for your interest in contributing to FENICE. This guide covers the standards and workflow expected for all contributions.
- Fork the repository to your own GitHub account
- Clone your fork locally
- Run
./setup.shto install dependencies and create your.env - Run
./dev.shto start the development environment - Create a feature branch from
development:git checkout -b feat/your-feature-name
- Check existing issues and PRs to avoid duplicate work
- For significant changes, open an issue first to discuss the approach
- Follow TDD (Test-Driven Development) when possible
- Write tests alongside or before your implementation
- Follow the existing code style (ESM,
.jsextensions on imports, strict TypeScript) - Use the adapter pattern for any new external service integrations
- Define Zod schemas as the single source of truth for new data structures
Run the full validation suite:
npm run validateThis runs:
npm run lint-- ESLint must pass with no errorsnpm run typecheck-- TypeScript strict mode must passnpm run test-- All tests must pass
All three must succeed before submitting a PR.
FENICE uses Conventional Commits. Every commit message must follow this format:
<type>: <description>
[optional body]
[optional footer]
| Type | When to Use |
|---|---|
feat |
New feature |
fix |
Bug fix |
docs |
Documentation only changes |
test |
Adding or updating tests |
refactor |
Code change that neither fixes a bug nor adds a feature |
chore |
Build process, tooling, dependency updates |
style |
Formatting, whitespace (no code logic changes) |
perf |
Performance improvement |
ci |
CI/CD configuration changes |
feat: add rate limiting middleware
Implemented sliding window rate limiter with configurable
thresholds per endpoint group.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: handle expired refresh token gracefully
Previously threw an unhandled error. Now returns 401 with
a clear error message.
- Strict mode is enabled (
strict: true,exactOptionalPropertyTypes: true) - Use explicit types over
any-- preferunknownwhen type is truly unknown - Optional properties must include
undefinedin the union:foo?: string | undefined
- All local imports must end in
.js:import { foo } from './bar.js'; - No CommonJS
require()calls
- Use
kebab-casefor all files:auth.routes.ts,console.adapter.ts - Suffix convention:
.routes.ts,.service.ts,.model.ts,.schema.ts,.adapter.ts,.test.ts
- Place unit tests in
tests/unit/mirroring thesrc/structure - Place integration tests in
tests/integration/ - Place property-based tests in
tests/properties/ - Use descriptive test names that explain the expected behavior
- Coverage thresholds: 80% for lines, branches, functions, and statements
Husky and lint-staged are configured to run automatically on every commit:
- ESLint with
--fixon staged.tsand.tsxfiles - Prettier on staged
.ts,.tsx,.json,.yml, and.yamlfiles
If the hooks fail, fix the issues before committing. Do not bypass hooks with --no-verify.
- Target the
developmentbranch (notmain) - Write a clear PR title using Conventional Commit format
- Include a description of what changed and why
- Ensure CI passes (lint, typecheck, test, build)
- Request review from a maintainer
- Address review feedback promptly
src/
schemas/ # Zod schemas (add new schemas here)
models/ # Mongoose models
services/ # Business logic
routes/ # OpenAPI route definitions
middleware/ # Hono middleware
adapters/ # External service abstractions
utils/ # Shared utilities
config/ # Environment and configuration
Open a GitHub Issue or check the existing documentation:
- CLAUDE.md -- Full project context
- docs/ARCHITECTURE.md -- Architecture decisions
- AGENTS.md -- API reference for AI agents