101 lines
5.2 KiB
Markdown
101 lines
5.2 KiB
Markdown
# Copilot instructions
|
|
|
|
## Build, test, and lint
|
|
|
|
Run repository-wide commands from the root:
|
|
|
|
```bash
|
|
make dev # build and run PostgreSQL, API, and frontend
|
|
make db-up # start only PostgreSQL
|
|
make migrate # apply Goose migrations
|
|
make migrate-down # roll back one migration
|
|
make test # backend and frontend unit/component tests
|
|
make test-integration # PostgreSQL-backed repository tests; database must be running
|
|
make lint # go vet and ESLint
|
|
make build # compile the API and production frontend
|
|
```
|
|
|
|
Run focused tests from the owning application:
|
|
|
|
```bash
|
|
cd backend && go test ./internal/service -run '^TestCandidatesRequireSixtyPercent$'
|
|
cd frontend && npm test -- src/App.test.tsx -t "filters jobs by title"
|
|
```
|
|
|
|
The required toolchain is Go 1.27.1 and React 19.3 with TypeScript/Vite. Frontend
|
|
formatting can be checked with `cd frontend && npm run format:check`; backend files
|
|
are formatted with `gofmt`.
|
|
|
|
## Architecture
|
|
|
|
The repository has two applications:
|
|
|
|
- `backend/` is a Go HTTP API. `cmd/api` wires configuration, PostgreSQL, security,
|
|
services, handlers, and routing. Requests flow through
|
|
`internal/web/{router,handler,model,mapper}` to
|
|
`internal/service/{model,services}` and then
|
|
`internal/database/{repository,mapper,entity}`. Do not bypass these mapping
|
|
boundaries by exposing GORM entities from handlers.
|
|
- `frontend/` is a React SPA. `src/App.tsx` owns routing/layout/screens,
|
|
`src/api.ts` is the typed HTTP boundary, and `src/types.ts` contains client domain
|
|
types. Vite proxies `/api` to the Go service in development.
|
|
|
|
Goose SQL files in `backend/migrations/` are the database schema source of truth.
|
|
The initial migration also supplies representative jobs, questions, participants,
|
|
traits, and the local interviewer account.
|
|
|
|
Authentication is custom bearer-token authentication: passwords are bcrypt hashes,
|
|
`POST /api/auth/login` issues an HMAC-signed JWT, and all domain routes require that
|
|
token. Never return password hashes or participant photo/CV bytes in JSON; those
|
|
files have dedicated endpoints.
|
|
|
|
CV ingestion stores the original bytes and extracts text from plain text, PDF, or
|
|
DOCX input. Trait extraction only considers normalized traits already present in
|
|
the database. Candidate matching compares a participant's extracted traits with a
|
|
job's labels, required skills, and additional skills; include matches only at the
|
|
`service.CandidateThreshold` of 60% or higher.
|
|
|
|
Questions are permanent, reusable library records and jobs own many-to-many links
|
|
to them. Detaching removes only the link. The lifecycle is
|
|
`draft -> published -> deprecated`; publishing requires at least one question, and
|
|
only published jobs can create invitations. Once an invitation exists, its question
|
|
set cannot be changed, and its duration is snapshotted so later job edits do not
|
|
alter an active interview.
|
|
|
|
Invitation URLs expose an opaque UUID token as `/participant/:invitationId` and use
|
|
public `/api/interviews/{token}` API routes; they do not use interviewer JWT
|
|
authentication. An invitation progresses from
|
|
`pending` to `accepted` to `completed`. Answers may only be saved while accepted
|
|
and before the snapshotted timer expires. The participant UI must gate questions
|
|
behind explicit acceptance and provide First, Previous, Next, Last, and Submit
|
|
controls. Interviewer review uses the protected
|
|
`GET /api/jobs/{id}/invitations/{invitationId}` endpoint, which intentionally
|
|
includes reference answers; the public interview endpoint does not.
|
|
|
|
## Repository conventions
|
|
|
|
- Every Go interface name starts with `I` (`IStore`, `IJobService`,
|
|
`IParticipantService`, `ITokenManager`).
|
|
- Keep transport, service, and persistence structs separate. Conversion belongs in
|
|
the adjacent `mapper` package, not in handlers or GORM entities.
|
|
- Question types are the exact API values `open`, `multiple_choice`, `radio`, and
|
|
`code_review`. Multiple-choice and radio questions require at least two options.
|
|
- Never delete question records through job operations. Use
|
|
`job_posting_questions` to attach/detach, and use the `deprecated` flag to retire
|
|
library questions. Full question edits use `PUT /api/questions/{id}`; deprecation
|
|
remains a separate `PATCH`.
|
|
- The question create/edit form builds multiple-choice and radio options one at a
|
|
time. Keep option editing state separate from the saved options array and require
|
|
at least two options for those types. Open and code-review questions instead
|
|
require a `referenceAnswer` textarea. Never include that interviewer-only answer
|
|
in public interview responses.
|
|
- Code-review questions also require `codeSnippet`. It is safe to include in the
|
|
participant payload and is rendered as a line-numbered pull-request review panel;
|
|
the participant's review comment remains the ordinary persisted answer.
|
|
- Traits are de-duplicated case-insensitively through their normalized database
|
|
value. Labels and both skill groups all reuse the same `traits` table.
|
|
- Participant creation is multipart form data using `firstName`, `lastName`,
|
|
`birthday`, `email`, `contactInfo`, `photo`, and `cv`.
|
|
- Return explicit errors. Handlers convert expected validation/not-found/authentication
|
|
failures to HTTP responses and log unexpected failures rather than silently
|
|
falling back to demo data.
|