diff --git a/.env.example b/.env.example index fa8c6404..6b587d22 100644 --- a/.env.example +++ b/.env.example @@ -93,8 +93,8 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000 # bring-your-own-key (BYOK) orgs are never affected. Leave unset to stay BYOK-only. # Get a key at https://openrouter.ai/keys # OPENROUTER_API_KEY=sk-or-... -# Default model for platform-paid runs (OpenRouter-prefixed). Default: openai/gpt-4.1-mini -# OPENROUTER_MODEL=openai/gpt-4.1-mini +# Default model for platform-paid runs (OpenRouter-prefixed). Default: openai/gpt-5.4-mini +# OPENROUTER_MODEL=openai/gpt-5.4-mini # Global platform-wide daily AI spend cap in USD — the runaway-loop kill-switch. # Trips loudly long before a bug can empty your account. Default: 25 # AI_DAILY_SPEND_CAP_USD=25 @@ -145,8 +145,21 @@ NUXT_PUBLIC_SITE_URL=http://localhost:3000 # SMTP_SECURE=false # true = implicit TLS (port 465), false = STARTTLS (port 587) # Option B: Resend (free tier: 3,000 emails/month — resend.com) +# A send-only key is sufficient for ordinary transactional email. # RESEND_API_KEY=re_xxxxxxxxxxxx # RESEND_FROM_EMAIL="Reqcore " +# Candidate messages use the recruiter's name as the display name while this +# setting supplies the mailbox and Reply-To routes each private conversation. +# RESEND_CANDIDATE_FROM_EMAIL="Reqcore Messages " +# Candidate messaging requires a dedicated receiving subdomain and a signed +# webhook subscribed to email.received, sent, delivered, delayed, bounced, +# failed, and complained events at /api/webhooks/resend. Add the Resend-provided +# MX record to the subdomain; do not use the domain of an existing mailbox. +# The Receiving API key must have Full access (a send-only key cannot retrieve +# inbound message bodies). +# RESEND_RECEIVING_API_KEY=re_xxxxxxxxxxxx +# RESEND_REPLY_DOMAIN=reply.yourcompany.com +# RESEND_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx # ─── Optional: Social Sign-In (Google, GitHub, Microsoft) ──────────────────── # Enable social login buttons on the sign-in and sign-up pages. diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dd8fde77..0d1bebe1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "1.5.0" + ".": "1.6.0" } diff --git a/.sync-upstream.conf b/.sync-upstream.conf new file mode 100644 index 00000000..bc203272 --- /dev/null +++ b/.sync-upstream.conf @@ -0,0 +1,2 @@ +UPSTREAM_REMOTE=upstream +UPSTREAM_BRANCH=main diff --git a/.zed/settings.json b/.zed/settings.json index 73bf94db..eca40b66 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -9,6 +9,8 @@ "**/Thumbs.db", "**/.classpath", "**/.settings", + "**/node_modules", + "**/.nuxt", "**/.output", "**/dist", "**/build", @@ -21,12 +23,12 @@ "settings": { "typescript": { "tsserver": { - "maxTsServerMemory": 3072 + "maxTsServerMemory": 2048 } }, "javascript": { "tsserver": { - "maxTsServerMemory": 3072 + "maxTsServerMemory": 2048 } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b36414..5eb5a131 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,64 @@ Format follows [Keep a Changelog](https://keepachangelog.com). Categories: **Add --- +## [1.6.0](https://github.com/reqcore-inc/reqcore/compare/v1.5.0...v1.6.0) (2026-07-18) + + +### ✨ Features + +* add ApplicationBuilderPreview and ApplicationFormBody components for candidate application preview and submission ([5118b6e](https://github.com/reqcore-inc/reqcore/commit/5118b6edb875296e5884f2eeed07a15c705b0857)) +* add branded career pages, ai config, and models ([7e9bcf6](https://github.com/reqcore-inc/reqcore/commit/7e9bcf627cd1ab66e972bfb431204611c2043847)) +* add end-to-end tests for privacy retention and GDPR compliance flows ([a0e2b58](https://github.com/reqcore-inc/reqcore/commit/a0e2b584cef92342fe46fe79daefb75ddc3c9272)) +* add public pricing section component and onboarding survey ([38e32a9](https://github.com/reqcore-inc/reqcore/commit/38e32a9e7b7a4cb23652cddebd9da32dda0cb197)) +* **ai:** implement budget management and observability for AI analysis runs ([f7a18f6](https://github.com/reqcore-inc/reqcore/commit/f7a18f6d70442931cb08749586f52bda34cb1abb)) +* **billing:** add billing plans and authorization logic ([86dd691](https://github.com/reqcore-inc/reqcore/commit/86dd6917befc8c2383872b6313ef0b6f95b3cbaa)) +* **billing:** add Stripe billing ([48a961c](https://github.com/reqcore-inc/reqcore/commit/48a961c8fe8c6a625cfc2dc423c25b1b3d96394a)) +* **billing:** enable Stripe promotion codes ([357c9b1](https://github.com/reqcore-inc/reqcore/commit/357c9b1ed61060570030670a50fe4ced54c8a9a8)) +* enable GDPR cleanup sweep in E2E tests and validate cleanup response ([dd16794](https://github.com/reqcore-inc/reqcore/commit/dd16794c4a8d720bc8ae196b650d178394212b5a)) +* enforce demo account isolation and organization access restrictions ([f47296e](https://github.com/reqcore-inc/reqcore/commit/f47296e91bcc3dad116d25152d8791a7d999c054)) +* enforce demo account isolation and organization access restrictions ([72433b9](https://github.com/reqcore-inc/reqcore/commit/72433b9c23bc0a9aaa28c03db36c97e86f01e0cf)) +* enhance GDPR retention and erasure processes with improved confirmation and settings ([6e110c3](https://github.com/reqcore-inc/reqcore/commit/6e110c36fe007b131080025a2bb4c3b147091370)) +* enhance job creation wizard with validation and schema updates; limit questions and criteria ([b128764](https://github.com/reqcore-inc/reqcore/commit/b12876452e1a35c4b9a7e578c6115379aebba03b)) +* enhance job question validation and update schemas; add question state validation and tests ([18bdd9f](https://github.com/reqcore-inc/reqcore/commit/18bdd9f7952bd69384f63223012b99c2fe4af10b)) +* handle organization update and delete errors with proper error handling ([989cca3](https://github.com/reqcore-inc/reqcore/commit/989cca3c21359e2dc788c96d91fa8596a9b17de5)) +* implement candidate erasure service and GDPR retention logic ([8692040](https://github.com/reqcore-inc/reqcore/commit/869204063470a88b060b03a33f353250545291ac)) +* implement candidate erasure service and GDPR retention logic ([b2910f8](https://github.com/reqcore-inc/reqcore/commit/b2910f8592b8a92936076c494f6710c5c11f7a79)) +* implement candidate retention and erasure processes ([7ee6ee3](https://github.com/reqcore-inc/reqcore/commit/7ee6ee315a1c9293e1777f8f1992b486fbda1085)) +* implement onboarding survey database storage ([ec6c3a0](https://github.com/reqcore-inc/reqcore/commit/ec6c3a0ee3d241e52e7f5bd4983530aa7f417904)) +* **jobs:** polish job creation wizard ([1926a8a](https://github.com/reqcore-inc/reqcore/commit/1926a8a3ff3d0a2fee39b4608e303dd48d899f0b)) +* Redesign application confirmation page ([0ae894f](https://github.com/reqcore-inc/reqcore/commit/0ae894fe1f84a2644def8d372ce576d55c72609f)) +* Refactor interview scheduling and add messaging ([88bdece](https://github.com/reqcore-inc/reqcore/commit/88bdece8b1b77e76954baafae3f9f1905b1f14fa)) +* transition to open-core model ([e0944b0](https://github.com/reqcore-inc/reqcore/commit/e0944b01461f80cdd0f899db7e54a839c5b85031)) +* update demo showcase to redirect to sign-in with prefilled demo credentials ([5625a56](https://github.com/reqcore-inc/reqcore/commit/5625a56aa57ac08a22fdeb943fe099b9f4e60934)) +* update dialog type for candidate erasure confirmation and refine legal hold handling in delete API ([bd2d156](https://github.com/reqcore-inc/reqcore/commit/bd2d156948545fd1b9e8367f35b2eec510aab7a1)) + + +### 🐛 Bug Fixes + +* align self-hosted billing copy ([b023b71](https://github.com/reqcore-inc/reqcore/commit/b023b71fd11036994d193ca7f642c97a06bd7d9d)) +* **e2e:** handle onboarding survey redirect after org creation ([3ed0630](https://github.com/reqcore-inc/reqcore/commit/3ed06307880b7d0cfb5bf5eff7b89d52bd5de450)) +* improve log handling in migration and S3 bucket readiness checks ([29fa699](https://github.com/reqcore-inc/reqcore/commit/29fa699bd64d8facb4828b668ddf1c4362ac9035)) +* refine rate limiting logic for production environment ([0d9c219](https://github.com/reqcore-inc/reqcore/commit/0d9c21917b10981788017940bc15eddb3a5b1eae)) +* sync npm lockfile ([e6d884e](https://github.com/reqcore-inc/reqcore/commit/e6d884e366a339edf1961976f1285d015d737306)) +* tolerate partial stripe billing env ([c9d702f](https://github.com/reqcore-inc/reqcore/commit/c9d702f20d2608d70918ac823d124c842d5040d6)) + + +### ♻️ Refactoring + +* remove JobQuestions component to streamline job wizard UI ([69ace61](https://github.com/reqcore-inc/reqcore/commit/69ace611d11ac8c557803b94e33d906cdbf16e11)) +* remove unused aiScoringChosen state and update form storage logic; add debug sign-in page script ([9b20abd](https://github.com/reqcore-inc/reqcore/commit/9b20abd91c3150596b1f882e80fc9cb1cfb7cae0)) +* update comments to clarify indexing rules for public marketing pages ([c919014](https://github.com/reqcore-inc/reqcore/commit/c919014db4ed60763802ab1577339893d341b396)) + + +### 🧪 Testing + +* enhance organization delete error handling in auth client tests ([d533052](https://github.com/reqcore-inc/reqcore/commit/d533052ec7d423310240d75bf7a4a7e54b4b5947)) + + +### 🏗️ Build & CI + +* wait for migration startup log ([24f5136](https://github.com/reqcore-inc/reqcore/commit/24f513685f9825040b60f9d3516689c80376523e)) + ## [1.5.0](https://github.com/reqcore-inc/reqcore/compare/v1.4.0...v1.5.0) (2026-05-17) diff --git a/INTERVIEW-SCHEDULING.md b/INTERVIEW-SCHEDULING.md new file mode 100644 index 00000000..3c31c220 --- /dev/null +++ b/INTERVIEW-SCHEDULING.md @@ -0,0 +1,68 @@ +# Simplified Interview Scheduling + +## Goal + +Make interview scheduling a natural part of the existing candidate conversation. Recruiters should not have to choose between email, calendar notifications, templates, or delivery methods. + +## Core Approach + +- Send every interview proposal through the two-way Reqcore inbox. +- Use the recruiter as the visible sender and route candidate replies back to the existing conversation. +- Attach an ICS calendar invitation so candidates can add the interview to Google Calendar, Outlook, Apple Calendar, or another calendar without a Reqcore calendar integration. +- Keep Reqcore as the source of truth for the interview and its confirmation state. +- Remove the separate no-reply invitation flow. + +## Recruiter Experience + +The scheduling form should ask only for: + +- Date and time +- Duration +- Interview format or location +- Interviewers +- An optional personal note + +Reqcore should automatically apply the organization defaults, detect the timezone, generate the message, attach the ICS invitation, and send it through the candidate conversation. + +## Candidate Experience + +The candidate receives one email containing the interview details, calendar invitation, and clear actions: + +- Confirm +- Request another time +- Decline + +They can also reply normally. Replies and interview responses remain visible in the same Reqcore conversation. + +Reschedules and cancellations should be sent through the same thread with an updated ICS invitation, using the same event identifier so calendar applications update the existing event. + +## Recruiter Outcome Actions + +- Cancelling an interview is candidate-facing after a proposal has been sent. Reqcore sends a cancellation message and calendar cancellation through the existing conversation. +- Marking an interview completed or no-show is an internal recruiting outcome. It does not contact the candidate. +- Recruiter controls must state whether the candidate will be contacted before the action is confirmed. +- A status change and its candidate-message delivery are separate outcomes. If delivery fails, the status remains accurate while the failed message stays visible and retryable. +- Deleting an interview is administrative and never contacts the candidate. A scheduled interview with a sent proposal must be cancelled before it can be deleted. + +## Calendar Integrations + +Google Calendar integration should be optional. The ICS invitation covers the candidate-facing calendar experience without OAuth or calendar webhooks. + +A connected calendar may later synchronize recruiter availability and internal events, but candidate communication and confirmation should continue to flow through Reqcore. + +## Reliability + +Interview state, message delivery, and calendar delivery should be tracked separately. Reqcore should never report that a proposal was sent when only the interview record was created. + +Failed messages should remain visible and retryable. Calendar attachment or synchronization failures should not lose the interview or conversation. + +## Free Plan + +The free plan can limit the number of tracked candidate conversations or interview processes. When the limit is reached: + +- Make upgrading the primary action. +- Explain that upgrading preserves replies, confirmations, calendar updates, and shared history. +- Allow the recruiter to continue outside Reqcore through a less convenient manual fallback. +- Never hide existing replies or block critical updates to an interview already in progress. + +The upgrade should sell a coordinated and reliable workflow rather than create a dead end during active hiring. diff --git a/README.md b/README.md index 032dbc03..0f2f6123 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A flood of applicants turns hiring into a full-time scroll. Reqcore takes every application on a role, runs it through an AI shortlist, and shows you exactly why each candidate was ranked the way they were — no black box. No per-seat fees, unlimited applicants on every plan. -Reqcore is open-core: the full hiring workflow — jobs, pipeline, applications, documents, job board, and AI shortlisting — is AGPLv3 and lives in this repo. A small set of paid, cloud-only features (SSO/SAML, audit log, source analytics, BYOK) live under [`ee/`](ee) on a separate commercial license. The core scoring and shortlist logic never depends on `ee/` — that's the part self-hosters and evaluators need to be able to trust and verify. +Reqcore is open-core: the full hiring workflow — jobs, pipeline, applications, documents, job board, and AI shortlisting — is AGPLv3 and lives in this repo. A small set of paid, cloud-only features (SSO/SAML, audit log, source analytics) live under [`ee/`](ee) on a separate commercial license. The core scoring and shortlist logic never depends on `ee/` — that's the part self-hosters and evaluators need to be able to trust and verify. > The fastest way to use Reqcore is the hosted product at [reqcore.com](https://reqcore.com) — free until your first shortlist, no card required. Prefer to run it yourself? See [Self-Hosting](#self-hosting) below. @@ -57,10 +57,10 @@ Reqcore is free to start and priced per active role, not per seat — invite you | Plan | Price | Active roles | Highlights | |------|-------|:---:|------------| -| **Free** | $0 | 1 | Unlimited applicants, one AI shortlist to try it | -| **Solo** | $79/mo | 2 | Unlimited AI shortlists, full shortlist workflow | -| **Team** | $239/mo | 8 | Deeper per-application analysis, custom domain, integrations | -| **Scale** | $599/mo | 24 | SSO/SAML/SCIM, audit log, DPA/SLA, bring your own AI key | +| **Free** | $0 | 1 | Unlimited applicants, one AI shortlist to try it, bring your own AI key | +| **Solo** | $79/mo | 2 | Unlimited AI shortlists, bring your own AI key, full shortlist workflow | +| **Team** | $239/mo | 8 | Bring your own AI key, deeper per-application analysis, custom domain, integrations | +| **Scale** | $599/mo | 24 | SSO/SAML/SCIM, audit log, DPA/SLA | | **Agency** | Contact us | Unlimited | Custom contract | See the live [pricing page](https://reqcore.com/pricing) for full details. Self-hosted instances use the same plan gates — see [Licensing & self-hosting](#licensing--self-hosting). diff --git a/app/app.vue b/app/app.vue index 333df6f9..a149004f 100644 --- a/app/app.vue +++ b/app/app.vue @@ -3,9 +3,25 @@ const i18nHead = useLocaleHead({ seo: true, }) +// Job listings/detail and branded career pages serve recruiter-authored, +// single-language content under every locale prefix, so their localized +// variants are noindex (see nuxt.config routeRules + the pages' robots meta). +// Strip the auto-generated hreflang alternates on those routes: advertising +// alternates that point at noindex URLs claims translations that don't exist, +// and Google drops hreflang clusters whose members aren't indexable. Every +// other route (marketing, /pricing) keeps its alternates; the canonical link +// and og:locale meta are left untouched. +const route = useRoute() +const isSingleLocaleUgc = computed(() => + /^\/(?:[a-z]{2}\/)?(?:jobs|career)(?:\/|$)/.test(route.path)) +const i18nLinks = computed(() => + isSingleLocaleUgc.value + ? i18nHead.value.link.filter((l) => !('hreflang' in l)) + : i18nHead.value.link) + useHead(() => ({ htmlAttrs: i18nHead.value.htmlAttrs, - link: i18nHead.value.link, + link: i18nLinks.value, meta: i18nHead.value.meta, })) diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 4f426885..f0721a2a 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @plugin "@tailwindcss/typography"; @source "../../../shared/properties.ts"; +@source "../../../ee/app/**/*.{vue,ts}"; /* ───────────────────────────────────────────────────────── Reqcore — Design System diff --git a/app/components/AiConfigForm.vue b/app/components/AiConfigForm.vue index 54d826f9..f4baf04f 100644 --- a/app/components/AiConfigForm.vue +++ b/app/components/AiConfigForm.vue @@ -44,6 +44,7 @@ interface AiConfigRow { isDefaultChatbot: boolean isDefaultAnalysis: boolean hasApiKey: boolean + source?: 'byok' | 'platform' } const props = defineProps<{ @@ -263,7 +264,7 @@ const badgeLabel = (badge?: ModelInfo['badge']) => {