Backcast is a Project Budget Management & Earned Value Management System for end-of-line automation projects. The system provides Git-style versioning with bitemporal entity tracking, branch isolation for change orders, and complete audit trails.
Tech Stack: Python 3.12+ / FastAPI (backend) + React 18 / TypeScript / Vite (frontend) + PostgreSQL 15+
# Virtual environment setup (prior to **every** backend command)
cd backend && source .venv/bin/activate
# Setup (from project root)
uv sync && docker-compose up -d postgres # Install deps and start PostgreSQL
# Database migrations
cd backend && uv run alembic upgrade head # Run migrations
uv run alembic revision --autogenerate -m "msg" # Create migration
# Development
uv run uvicorn app.main:app --reload --port 8020 # Start dev server (port 8020)
# Logs (troubleshooting)
tail -f backend/logs/app.log # Live app logs
# Testing
uv run pytest -k "test_name" # Run specific test
uv run pytest --cov=app # With coverage
# Code Quality (REQUIRED before commits)
uv run ruff format . && uv run ruff check . && uv run mypy app/ # Format, lint, and type check (must pass)cd frontend
npm run dev # Start dev server (port 5173)
npm test # Run unit tests (Vitest)
npm run test:coverage # With coverage
npm run build # Production build
npm run generate-client # Generate types from OpenAPI spec
# Code Quality (REQUIRED before commits)
npm run lint # ESLint - must pass
npm run typecheck # TypeScript type checking# Backend: cd backend && uv run ruff check . && uv run mypy app/ && uv run pytest
# Frontend: cd frontend && npm run lint && npm run test:coverageBackend: Layered architecture (API → Services → Models). No repository pattern — services access the database directly via AsyncSession.
Frontend: Feature-based organization with TanStack Query for server state, Zustand for client state.
EVCS (Entity Versioning Control System): Bitemporal versioning with PostgreSQL TSTZRANGE. All versioned entities support branch isolation for change orders.
- Connection: AsyncPG with optimized pooling
- Migrations: Alembic (create with
--autogenerate) - Indexes: GIST indexes for range queries, partial indexes for current versions
- Constraints: Exclusion constraints for temporal ranges
- Querying: use postgres mcp tool to investigate database schema and data
- Base URL:
/api/v1 - Authentication: JWT Bearer tokens (
Authorization: Bearer <token>) - Response Format: JSON with standardized error handling
- OpenAPI Docs: Available at
/docs(Swagger UI) and/openapi.json
The codebase and test suite are large. To improve efficiency, perform quality checks (linting, type checking) and execute tests ONLY on the modified codebase and its relevant scope. Do not run full project-wide checks or the entire test suite unless explicitly requested or necessary for integration verification.
- Backend: MyPy strict mode (zero errors), Ruff (zero errors), 80%+ test coverage
- Frontend: TypeScript strict mode, ESLint clean, 80%+ test coverage
- Testing:
pytest-asynciostrict mode for async tests
Ruff Configuration: Line length 88, ignores B008 (for FastAPI Depends())
EVCS Entity Tiers:
- Simple:
SimpleEntityBase→SimpleService(app/core/base/base.py,app/core/simple/service.py) - Versionable:
EntityBase + VersionableMixin→TemporalService(app/core/versioning/service.py) - Branchable:
EntityBase + VersionableMixin + BranchableMixin→BranchableService(app/core/branching/service.py) - Entity guide:
docs/02-architecture/backend/contexts/evcs-core/entity-classification.md
EVCS Relationships:
Versioned entities (Project, WBSElement, CostElement) use two identifiers:
- Root ID (
project_id,wbs_element_id,cost_element_id): Stable identifier across all versions. Used in foreign keys, URLs, and API endpoints. References the entity's identity, not a specific version. - Version ID (
id): Primary key of each version row. Unique per version, but not used in relationships.
Relationships between versioned entities use root IDs, not version IDs. No database-level FK constraints because root_id is not unique across versions (integrity enforced at application level). Example: WBSElement.project_id references the root project_id, not a specific version's id.
- Context7 MCP: Up-to-date library documentation and code examples
- Call
mcp__plugin_context7_context7__resolve-library-idwith library name to get ID - Call
mcp__plugin_context7_context7__query-docswith library ID and query
- Call
- webReader: Fetch current documentation from websites
- Call
mcp__web_reader__webReaderwith URL to fetch web content as markdown
- Call
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
Touch only what you must. Clean up only your own mess.
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
These guidelines are working if: fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
Interview me until you have 95% confidence about what I actually want, not what I think I should want.
When receiving a request:
- Don't assume the stated solution is the right one
- Ask clarifying questions to understand the underlying problem
- Surface tradeoffs between different approaches
- If something seems off, say so - don't blindly implement
- The goal is to solve the actual problem, not just follow instructions
ALWAYS use specialized agents for code modifications.
When making changes to the codebase:
- Frontend changes (React, TypeScript, UI components): Use the
frontend-developeragent - Backend changes (Python, FastAPI, services, models): Use the
backend-developeragent
You may directly edit files for:
- Configuration files (CLAUDE.md, package.json, tsconfig.json, etc.)
- Documentation files
- Simple typo fixes
- Emergency hotfixes when agents are unavailable