- MSAL.js (PKCE) browser-side auth against Azure Entra ID - Bearer token interceptor on all API calls - Backend JWT validation middleware (python-jose + JWKS) - All API routes protected; /api/health stays public - vite base set to /gsb/, BrowserRouter basename=/gsb - docker-compose: remove frontend service, lock backend to 127.0.0.1:8002, remove dev volumes - backend: 2 workers, no --reload Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.3 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Overview
GMAL Scope Builder is a Dockerized AI-powered scoping tool that matches client deliverables (from uploaded Word/Excel documents) against a standardized GMAL asset database, then builds team ratecards and FTE models. The AI layer uses Claude Opus 4.6 for both document parsing and asset matching.
Development Commands
Docker (primary workflow)
docker compose build # Build all images
docker compose up -d # Start all services (bg)
docker compose logs backend --tail 50 # Backend logs
docker compose logs frontend --tail 20 # Frontend logs
docker compose down # Stop all services
Services run on
- Frontend: http://localhost:3010
- Backend API: http://localhost:8001
- PostgreSQL: localhost:5433
One-time setup
cp .env.example .env # Add ANTHROPIC_API_KEY
# Place GMAL Excel file in data/ directory
curl -X POST http://localhost:8001/api/gmal/ingest # Populate GMAL catalog
Frontend (without Docker)
cd frontend && npm install
npm run dev # Vite dev server with HMR
npm run build # TypeScript compile + production bundle
Backend (without Docker)
cd backend && pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
Database operations
# Backup
docker compose exec db pg_dump -U scope_user -d scope_builder > backups/dump.sql
# Restore
docker compose exec -T db psql -U scope_user -d scope_builder < backups/dump.sql
Architecture
Stack
- Frontend: React 18 + TypeScript + Vite + React Router + Axios
- Backend: FastAPI + SQLAlchemy (async) + asyncpg + Uvicorn
- Database: PostgreSQL 16
- AI: Claude Opus 4.6 via Anthropic SDK (tool_use for structured output)
- Document parsing: openpyxl, python-docx
Backend structure (backend/app/)
main.py: FastAPI app, CORS config, router registration, AI usage/debug endpointsmodels/: SQLAlchemy ORM —gmal.py(catalog: GmalAsset, Role, GmalHours, ServiceLine) andproject.py(workflow: Project, ClientAsset, Match, RatecardLine)services/: Core business logic — see flow belowapi/: Route handlers forgmal,ingest,projects,matching,ratecardschemas/: Pydantic request/response modelsutils/claude_client.py: Wraps Anthropic SDK with per-project + global token/cost tracking and a 50-call debug log
Frontend structure (frontend/src/)
App.tsx: Router, navigation bar, live AI cost tracker, expandable debug panelpages/: Dashboard, NewProject, ProjectView (main workflow), GmalBrowser, GmalEditor, Helpapi/client.ts: Axios instance pointing to backendtypes/index.ts: Shared TS interfaces +MODEL_TYPE_LABELS/CONFIDENCE_COLORSconstants
Core data flow
-
Ingestion — Excel file →
excel_parser.py→GmalAsset+Role+GmalHours(per model type) in PostgreSQL -
Project creation — User selects one of 5 model types (Current, AI-Enhanced, Offshore+, Local, Factory); this key drives which
GmalHoursrows are used throughout -
Document parsing — Uploaded
.docx/.xlsx→doc_parser.pyextracts raw text → Claude withextract_assetstool returns structuredClientAssetlist (name, description, volume, complexity hint) -
AI matching — Each
ClientAsset→ai_matching.py→ Claude withsubmit_matchestool → ranked GMAL matches with confidence (exact/close/multiple/none), score 0–1, reasoning, caveats. Processed in batches of 10 with cancellation support. -
Ratecard building — User selects a match per asset →
ratecard_builder.pylooks upGmalHours[gmal_asset, model_type], multiplies byClientAsset.volume→RatecardLinerows (one per role per asset) -
Team shape —
team_shape.pyaggregates hours per role → FTE = total / 1800; efficiency slider (0–90%) is applied to delivery roles only (programme roles are not reduced) -
Export —
export_excel.pyproduces multi-tab workbook (ratecard, asset detail, team shape, efficiency);export_pdf.pyproduces caveats report
Project status lifecycle
draft → parsing → matching → review → building → finalized
AI cost tracking
Every Claude call records input/output tokens and USD cost ($3/M input, $15/M output) via claude_client.py. Costs are stored on the Project model and surfaced globally via GET /ai/usage. The frontend polls this and shows a live cost tracker + expandable debug panel.
Key design decisions
- All Claude calls use
tool_usefor structured output — no fragile JSON parsing from free-text responses model_typeis set at project creation and cannot change — it filters allGmalHourslookups- Programme roles are exempt from efficiency reduction in team shape calculations (they don't scale with AI productivity)
- Matching is async/batched — supports cancellation mid-job; poll
/api/projects/{id}/statusfor progress - The GMAL catalog (390 assets) is ingested from a single Excel file in
data/; re-run/api/gmal/ingestto reload after updating the file