Development
Notes for contributors. See also CONTRIBUTING.md and the root AGENTS.md.
Layout
Section titled “Layout”src/app/Next.js 16 app (App Router): pages, API routes, server actions. Next.js 16 differs from older versions; readnode_modules/next/dist/docs/before using its APIs.src/runner/the scheduler / collector process (container);src/ai-worker/the host AI process.src/lib/domain code:channels/(one folder per source),pipeline/(normalize, de-duplicate, lifecycle),filter/,signals/,scoring/,ai/(provider layer),notify/(Telegram),cycle/(processing cycle steps),config/(zod schemas and loaders),db/(schema, queries, repos),ops/(backup, doctor, disk, logs),settings/.config/shipped default configuration;jobspy/the Python scraping sidecar;drizzle/SQL migrations;messages/{en,zh}.jsonevery user-visible string;scripts/CLI helpers,scripts/ops/operations scripts.
Rules that shape the code
Section titled “Rules that shape the code”- The raw posting is stored first; parse failures keep the payload.
- A channel failure only affects that channel.
- Facts the posting does not state are
unknown; AI judgements live in their own tables. - Secrets only in
.envand.auth/, never in logs or error messages. - Personal data stays local.
- TypeScript strict, no
any, zod at every external boundary (HTTP, email, AI output, configuration, forms). - Signals must carry a quote from the job description.
- Thresholds, budgets, times, keywords, countries, currencies and user-visible text are configuration or messages,
never hard-coded. Tests build the configuration they need; they never assert values of the shipped
config/*.yaml. - Database access only in
src/lib/db/.
Running things
Section titled “Running things”pnpm installdocker compose up -d db # Postgres on 127.0.0.1:5433 for host-side toolspnpm db:migratepnpm db:seed # ~20 fictional postings for UI workpnpm dev # dashboard on http://localhost:3000pnpm runner # collector on the host (optional)pnpm ai-worker # host AI worker (optional)pnpm cycle:run --channel fixture --dry-run # rehearse a cycle on the fixture channelTry it with sample data
Section titled “Try it with sample data”To look around without your own data or any API key:
docker compose up -d dbpnpm db:migratepnpm db:seed # fictional companies, jobs, runs and a few applicationspnpm config:example ai-engineering-europe # optional: a ready-made search setuppnpm dev # open http://localhost:3000The first visit may open the setup; every step can be skipped. pnpm db:seed only replaces what an earlier seed wrote.
README screenshots
Section titled “README screenshots”pnpm demo:shots regenerates the pictures in docs/images/ (the README screenshots and the demo GIF) from a throw-away
instance with fictional data: its own jobradar_demo database, config and profile in temp directories, and an empty
environment (your .env and data/ are never read). It needs ffmpeg and a Postgres server where the role may create a
database:
DEMO_ADMIN_DATABASE_URL=postgres://jobradar:<password>@127.0.0.1:5433/postgres pnpm demo:shotsBefore any picture is written, a guard checks the page markup and responses for machine paths, the user and host name,
real-looking e-mail addresses and the strings of .release-check.local (required by default, also found from a git
worktree; --allow-missing-patterns to run without); a hit stops the run. The guard does not read JS, CSS, fonts,
canvas, iframes or image pixels, and only .env.example variable names are blanked for the server. The run builds into
the repository’s .next/, so don’t run it next to pnpm dev / pnpm start. Details, names and budgets:
images/README.md. It is not part of pnpm test:e2e.
Checks
Section titled “Checks”scripts/validate.sh runs the i18n catalog check (pnpm i18n:check), lint, typecheck, unit tests, config check, the
documentation link check, the CI documentation check (pnpm ci:docs-check) and the planning-id check
(scripts/release-check.sh --planning-ids-only: internal tracker ids such as F- / B- / ADR- numbers must not appear in the repository; a path whose id
is data goes into scripts/planning-ids.allow). User-visible text lives in messages/*.json (see languages.md); after editing
a messages file run pnpm i18n:catalog. In addition:
pnpm test:integration # needs the compose db; creates and drops its own databasepnpm build && pnpm build:runnerpnpm test:e2e # Playwright against a production build and a throw-away databaseE2E_PORT=<port> gives the e2e run its own database, config and profile directories, so several checkouts can run in
parallel.
To run only some e2e tests, name them: pnpm test:e2e e2e/list-today.spec.ts (one file), several files, or
e2e/dashboard.spec.ts:206 (the test at that line). Only those tests run; the web server, the database preparation and
the stand-in runner start as for the full suite. This works for every spec, including the ones in the serial chain
(settings, interests, sources, market): e2e/projects.ts keeps just the projects with a named spec. pnpm exec playwright test <file> behaves the same. --project <name> also runs that project’s dependencies (the whole parallel suite first);
add --no-deps to skip them. --grep on its own does not narrow the dependencies (every project before a matching
one runs in full): name the file as well. The named files are read from the command line unless E2E_SPEC_FILTERS is set in the
environment (a JSON list of filters, used to hand them to the workers): an exported value overrides the command line. The integration tests need pg_dump / pg_restore of the server’s major version (17): set PG_DUMP_PATH /
PG_RESTORE_PATH, or use scripts/ops/pg-docker.sh symlinked as pg_dump and pg_restore (what CI does).
What the CI runs, job by job, and how to run it locally: ci.md. Changing a workflow means updating that page
(pnpm ci:docs-check enforces it).
Migrations
Section titled “Migrations”Edit src/lib/db/schema.ts, then pnpm db:generate --name <slug> and commit the generated SQL. CI fails when the
schema and drizzle/ disagree. The runner applies migrations at start-up.
Before a public release
Section titled “Before a public release”scripts/release-check.sh scans the working tree and the whole commit history for secrets (gitleaks, through Docker), for
personal information (names, machine paths, host names), for private planning ids anywhere in the repository, and checks that
.gitignore covers .env, data/ and .auth/.
Synced from docs/ in Joevonlong/job-radar@2947f47. Job Radar is not affiliated with any job board. Code under AGPL-3.0-only.