go-guess/.github/copilot-instructions.md
2026-10-02 21:44:26 +02:00

6.2 KiB

Copilot instructions

Build, test, and lint

Run repository-wide commands from the root:

make dev                 # run PostgreSQL, RabbitMQ, API, and frontend with development fixtures
make test                # complete suite, including disposable infrastructure integration tests
make test-integration    # Go tests; starts PostgreSQL and RabbitMQ Testcontainers
make test-env            # isolated seeded UI stack at http://localhost:15173
make test-env-down       # remove the test stack and its PostgreSQL volume
make lint                # go vet and ESLint
make build               # compile the API and production frontend

Focused tests:

cd backend && go test ./internal/service -run '^TestCandidatesRequireSixtyPercent$'
cd backend && go test -tags=integration ./internal/integration -run '^TestCompleteInterviewWorkflow$'
cd frontend && npm test -- src/app/App.test.tsx -t "filters jobs by title"

The required toolchain is Go 1.27.1 and React 19.3 with TypeScript/Vite. Use cd frontend && npm run format:check for frontend formatting and gofmt for Go.

Architecture

  • backend/cmd/api wires configuration, PostgreSQL, security, services, handlers, and the Chi router. Requests flow through internal/web/{router,handler,model,mapper} to internal/service/{model,services} and then internal/database/{repository,mapper,entity}. Never expose GORM entities from handlers.
  • backend/cmd/seed applies embedded environment-aware fixtures.
  • backend/internal/eventbus publishes note and shared-document events to the durable go-guess.interview-events RabbitMQ fanout exchange. Each API instance consumes through its own exclusive queue and distributes interview-filtered events to authenticated interviewer SSE streams or token-authorized participant streams.
  • frontend/src/app owns application routing and providers, src/features groups feature pages and components, src/components contains reusable UI, src/api/client.ts is the typed HTTP boundary, src/types contains client domain types, and src/styles/global.css contains application-wide styles. Keep feature-specific components inside their feature directory. Vite proxies /api to the Go service in development.
  • Local multi-tenant URLs use <tenant>.guess.local. The frontend derives the tenant from the hostname, calls same-origin /api through the HTTPS proxy, and sends X-Tenant-Id on every request type. Other deployments use same-origin /api. Generated participant links must use the validated matching tenant origin. Keep this contract documented in README.md and frontend/README.md.
  • Protected interviewer routes also require Go Loose X-API-Key authorization for application guess. The tenant passed to Go Loose comes from X-Tenant-Id. GO_LOOSE_BASE_URL defaults to https://%s.auth.dev; %s is that tenant and must be one DNS label. Do not require the API key on /api/health, /api/openapi.json, /api/auth/login, or opaque-token participant routes. The frontend sends the key only when VITE_GO_LOOSE_API_KEY is present at build time. Document both variables when their contract changes.

Goose files in backend/migrations are schema-only and run in every environment. internal/database/seed/common.sql contains shared reference data; development.sql and test.sql contain idempotent environment-specific fixtures. Every entity change requires a migration plus corresponding fixture and integration test updates. Never put demo users or test records in schema migrations.

Authentication uses bcrypt passwords and HMAC-signed bearer JWTs. Interviewer routes additionally require a Go Loose API key for the request tenant. Health, OpenAPI, login, and opaque-token participant interview routes do not. Never expose password hashes, CV/photo bytes, interviewer reference answers, or API keys in public JSON or logs.

Questions are permanent reusable records linked to jobs through job_posting_questions; detach links rather than deleting questions. Deprecated questions remain searchable but cannot be newly attached. Question values are open, multiple_choice, radio, and code_review. Choice types require at least two options. Open/code-review types require referenceAnswer; code review also requires codeSnippet.

Jobs move through draft, published, and deprecated. Publishing requires a question, only published jobs can create invitations, and candidate matching uses labels plus both skill groups with a 60% threshold. Invitation URLs use /participant/:invitationId, where the identifier is an opaque token. Participant questions are acceptance-gated and answers are saved before final submission; interviewer review uses GET /api/jobs/{id}/invitations/{invitationId}.

Only completed assessment invitations can receive a passed or failed outcome, and only passed candidates can be scheduled for an interview. Scheduled interviews include the creator plus selected co-interviewers. Attendees manage their invitation through /api/invitations, see accepted interviews in /api/schedule, and can write notes only after the interview has started. Notes are private to authenticated attendees. The opaque /participant/meeting/:candidateToken view exposes only candidate-safe schedule and shared-document data.

Repository conventions

  • Prefix every Go interface name with I.
  • Keep web, service, and persistence models separate and convert them in adjacent mapper packages.
  • Participant creation uses multipart fields firstName, lastName, birthday, email, contactInfo, photo, and cv.
  • Return explicit errors; map expected failures to HTTP status codes and log unexpected failures instead of silently falling back.
  • Update the directly related local documentation whenever behavior, configuration, architecture, commands, or public API surfaces change.
  • Register backend routes through the typed OpenAPI-aware router helper. The runtime /api/openapi.json document is the API source of truth; do not add a manually maintained route inventory or require a generation command.
  • Commit every repository change with a meaningful conventional prefix such as feat:, fix:, refactor:, test:, docs:, or chore:.