Files
TS_Proto/CLAUDE.md
T
nilesh.pandClaude Opus 4.8 035711f08c Add project guidance, security/governance/hygiene rules, and product wiki
- CLAUDE.md + AGENTS.md: canonical guiding principles for AI agents and
  developers (tenant isolation, no RLS, stack, security, conventions,
  environment gotchas)
- SECURITY.md: required security controls (isolation, secrets, auth,
  input handling, least privilege) with VAPT status
- GOVERNANCE.md: how changes are validated before merge — deterministic
  gates + AI review, risk tiers, human accountability
- CONTRIBUTING.md: code hygiene, definition of done, file hygiene
- docs/wiki/: product reference (overview, domain model, features,
  roles & access, API reference, glossary)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 13:31:04 +05:30

184 lines
8.4 KiB
Markdown

# TravelHub — project guide for AI agents and developers
Guiding principles and working conventions for this repository. Read this
before generating or changing code here. It is the canonical guide; other
tools' entry points (`AGENTS.md`) point to it.
TravelHub is a **multi-tenant flight-booking platform**: an Angular SPA, a Go
API, and PostgreSQL. Several travel agencies ("tenants") share one deployment
and one database, and **no tenant may ever see another's data**. That promise is
the point of the product and the first thing to protect in any change.
**Required reading** (rules are enforced, not advisory):
[SECURITY.md](SECURITY.md) · [GOVERNANCE.md](GOVERNANCE.md) ·
[CONTRIBUTING.md](CONTRIBUTING.md). Product reference:
[docs/wiki/](docs/wiki/README.md). Testing: [TESTING.md](TESTING.md),
[qa/README.md](qa/README.md).
---
## Stack (do not swap without being asked)
- **Frontend:** Angular 22 (standalone, zoneless), TypeScript, SCSS.
- **Backend:** Go 1.26, module `github.com/travelswitch/travelhub/backend`,
`pgx/v5`, standard-library HTTP (`net/http`), `log/slog`.
- **Database:** PostgreSQL 17.
- **Tests:** Go (integration, real DB), Angular (Vitest), Playwright (e2e).
New work for this team defaults to this stack and to multi-tenancy; design
tenant isolation in from the start, never retrofit it.
---
## Guiding principles
1. **Tenant isolation is enforced in application code, and it is sacred.**
- The tenant id comes from the **authenticated identity**, never from the
URL, query string, or request body. A client cannot name its own tenant.
- **Every** query touching a tenant-owned table (`users`, `flights`,
`bookings`, `passengers`) filters on `tenant_id` as its first parameter.
- A lookup that misses within the tenant returns `ErrNotFound` → **HTTP 404**,
never 403 — so probing for another tenant's record is indistinguishable
from asking for one that does not exist.
- **Do not introduce PostgreSQL row-level security (RLS).** It is a deliberate
choice for this project: per-request session state (`SET LOCAL`) fights
connection pooling and a missed reset leaks across tenants rather than
failing closed. Enforce isolation in the repository layer with the
convention above. If stronger isolation is wanted at scale, prefer
schema- or database-per-tenant, not RLS.
2. **Security is not optional.** See the security rules below and
`security/VAPT-REPORT.md`. Never weaken a security control to make something
work; flag it instead.
3. **A change isn't done until its tests pass and prove it.** Assert on
behaviour a user can observe, not on implementation. Never loosen an
assertion to get green.
4. **Scope discipline.** Do what was asked; don't widen, narrow, or sneak in
unrelated changes. AI-generated PRs that drift in scope are rejected.
5. **The human is accountable for what merges.** AI generates and reviews;
people own the merge decision on anything security- or tenant-sensitive.
---
## Layout
```
backend/
cmd/api/ server entrypoint, graceful shutdown
internal/
httpapi/ the route tree (shared by server and tests)
handlers/ HTTP layer — takes tenant id from the identity
middleware/ basic auth + tenant resolution, CORS, recoverer
repository/ ALL SQL; every tenant query filters tenant_id = $1
auth/ identity carried in request context
config/ .env loading, DSN assembly
database/ pgx pool
models/ API types (camelCase JSON)
testsupport/ per-test tenant harness for the Go suite
migrations/ 001 schema · 002 seed · 003 demo bookings (idempotent)
frontend/ Angular SPA (features/, core/, layout/, shared/)
e2e/ Playwright: pages/ fixtures/ tests/ reporters/
CLAUDE.md conventions for generating Playwright tests
qa/ scenario cards, the generation prompt, the QA runbook
scripts/ db.mjs, report + hook helpers
app.ps1 start/stop/restart/rebuild/status/logs the app
run-tests.ps1 run any test layer, log, emit JUnit + evidence
```
---
## Running, testing, data
Everything has an npm script or a ps1; prefer them over ad-hoc commands.
```powershell
.\app.ps1 start # DB up, API (:8080) + Angular (:4200)
.\app.ps1 rebuild -Clean # build Go binary + Angular bundle
.\run-tests.ps1 -Layer all # backend + web + ui, logged, JUnit + evidence
.\run-tests.ps1 -Layer unit # fast: backend + web, no browser
npm run db:migrate | db:reset # schema + seed (reset re-seeds fresh)
npm run report # open the evidence report
```
- **Test output** lands in `.testlogs/<timestamp>/` (logs, `summary.json`,
`junit.xml`) and `e2e/evidence-report/` (screenshots + API request/response).
All are git-ignored run artifacts.
- **Demo data is time-relative** — seeded flights depart within 21 days of seed
time and go stale after ~3 weeks. `run-tests.ps1` re-seeds automatically via
`npm run db:check`; if the UI shows no flights, run `npm run db:reset`.
---
## Conventions
**Go.** `gofmt` + `go vet` clean. Errors wrapped with `%w` and context. SQL only
in `repository/`, always parameterised (`$1…$n`) — never string-concatenate
values into SQL. Whitelist anything that must reach the SQL text (e.g. sort
columns). Return `ErrNotFound`/`ErrNoSeats`; handlers map them to status codes.
**Angular.** Standalone components, signals, lazy-loaded routes. Services in
`core/`. The auth interceptor stamps credentials + `X-Tenant`; handlers never
trust a client-sent tenant. Keep SCSS theme-aware and responsive (no horizontal
scroll at 390px — grid tracks use `minmax(0, 1fr)`, wide tables scroll in their
own container).
**SQL/migrations.** Idempotent and re-runnable. Multi-tenant tables carry
`tenant_id` with `ON DELETE CASCADE`; global reference data (airports, airlines)
does not. Unique constraints are per-tenant (e.g. PNR unique within a tenant).
**Tests.** See `e2e/CLAUDE.md` for Playwright specifics and `qa/README.md` for
the scenario-card workflow. Go tests get a fresh tenant each via `testsupport`.
---
## Security rules (hard)
- **Never commit secrets.** `.env` and `*.env` are git-ignored (only
`.env.example` is tracked). Credentials, tokens and DB passwords live in
`.env`, read at runtime. The `pre-commit` hook blocks committed secrets; don't
defeat it.
- **Never print or store a live credential.** The e2e API-capture redacts the
`Authorization` header (it's the Basic-auth secret); keep it redacted.
- Passwords are bcrypt; hash in Go, and keep the real and the timing-dummy hash
at the **same cost** (a cost mismatch created a username-timing oracle — see
the VAPT).
- For production, the app must connect to PostgreSQL as a **least-privilege
role**, not a superuser, and over TLS. The open findings are tracked in
`security/VAPT-REPORT.md`.
---
## Git & CI
- Branch from `main`; don't commit or push unless asked. On push, if the remote
has moved, **rebase** onto it (keep history linear) rather than force-push.
- The Gitea remote is `origin`; auth uses `GITEA_TOKEN` from `.env`. Never embed
the token in `.git/config` or in output — pass it inline for one push,
redacted.
- Install hooks once per clone: `.\scripts\install-hooks.ps1`
(pre-commit = secret scan, pre-push = unit tests). `core.hooksPath` is local
and does not travel with a push.
- Commit/PR attribution lines are set by the session; follow whatever the
current reminder specifies.
- CI contract: `run-tests.ps1` emits `junit.xml` + the evidence report for any
pipeline to publish. Azure DevOps integration is planned but not yet wired.
---
## This machine (known gotchas)
- **Windows + PowerShell 5.1.** No `&&`/`||`/ternary. `app.ps1` and
`run-tests.ps1` refresh `PATH` from the registry themselves, because a
terminal opened before Go/Node were installed won't see them — if a fresh
shell can't find `go`/`npm`, that's why (or restart VS Code).
- **Native stderr isn't failure.** Treat a tool's exit code as truth; a warning
on stderr must not throw (use `Continue` around native calls).
- **`npx playwright show-report` ≠ the evidence report.** That opens Playwright's
own debug report from `e2e/playwright-report/`. The rich report (screenshots +
API traffic) is `e2e/evidence-report/`, opened with `npm run report`.
- `psql` is at `C:\Program Files\PostgreSQL\17\bin`; the PostgreSQL service is
`postgresql-x64-17`.