Skip to content

Add new features and documentation for AI intelligence, collaboration, pipeline management, recruitment tools, and security compliance#42

Merged
JoachimLK merged 1 commit into
mainfrom
feat/feature-catalog
Feb 26, 2026
Merged

Add new features and documentation for AI intelligence, collaboration, pipeline management, recruitment tools, and security compliance#42
JoachimLK merged 1 commit into
mainfrom
feat/feature-catalog

Conversation

@JoachimLK

@JoachimLK JoachimLK commented Feb 25, 2026

Copy link
Copy Markdown
Contributor
  • Introduced AI Candidate Ranking (Glass Box) feature with detailed implementation plans and design notes.
  • Added Local AI via Ollama for privacy-focused AI model execution.
  • Implemented Resume Parsing functionality to automate candidate data extraction from resumes.
  • Created Candidate Portal for self-service application status checks and updates.
  • Developed Email Notifications system for key hiring events.
  • Established Application Tracking and Candidate Profiles for better management of candidate data.
  • Launched Kanban Pipeline Board for visual tracking of candidates through hiring stages.
  • Enhanced Job Management with structured workflows and CRUD capabilities.
  • Built Public Job Board for SEO-friendly job listings and application submissions.
  • Implemented GDPR Data Management features for compliance with data protection regulations.
  • Established API Rate Limiting and Server-Proxied Documents for enhanced security and data privacy.

Summary

  • What does this PR change?
  • Why is this needed?

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Chore

Validation

  • I tested locally
  • I added/updated relevant documentation
  • I verified multi-tenant scoping and auth behavior for affected API paths

DCO

  • All commits in this PR are signed off (Signed-off-by) via git commit -s

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new "Catalog" section to explore all planned and shipped features, organized hierarchically by category.
    • Feature detail view displays metadata (status, priority, complexity) and includes a discussion area for feedback.
  • Documentation

    • Expanded documentation covering AI capabilities, collaboration tools, pipeline management, platform infrastructure, recruitment features, and security compliance.

…, pipeline management, recruitment tools, and security compliance

- Introduced AI Candidate Ranking (Glass Box) feature with detailed implementation plans and design notes.
- Added Local AI via Ollama for privacy-focused AI model execution.
- Implemented Resume Parsing functionality to automate candidate data extraction from resumes.
- Created Candidate Portal for self-service application status checks and updates.
- Developed Email Notifications system for key hiring events.
- Established Application Tracking and Candidate Profiles for better management of candidate data.
- Launched Kanban Pipeline Board for visual tracking of candidates through hiring stages.
- Enhanced Job Management with structured workflows and CRUD capabilities.
- Built Public Job Board for SEO-friendly job listings and application submissions.
- Implemented GDPR Data Management features for compliance with data protection regulations.
- Established API Rate Limiting and Server-Proxied Documents for enhanced security and data privacy.
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new feature catalog page is introduced alongside supporting components and extensive feature documentation. The changes add a catalog UI displaying hierarchical feature information with sidebar details, integrate Giscus for discussions, add navigation links across multiple pages, configure content collection for the catalog, and include comprehensive documentation for AI intelligence, collaboration, pipeline management, platform, recruitment tools, and security compliance features.

Changes

Cohort / File(s) Summary
Catalog Page Component
app/pages/catalog/index.vue
New Vue 3 SFC with hierarchical tree structure for categories/features, interactive sidebar for feature details, status/priority/complexity badges, markdown rendering, competitor comparison tables, and Giscus-powered discussions.
Comment Widget Component
app/components/GiscusComments.vue
New client-only Vue component that loads and renders Giscus widget with configurable term prop; reloads widget when term changes.
Navigation Updates
app/pages/index.vue, app/pages/blog/index.vue, app/pages/blog/[...slug].vue, app/pages/roadmap.vue
Added "Features" navigation link to /catalog across home, blog index, blog article, and roadmap pages.
Content Configuration
content.config.ts, nuxt.config.ts
Added catalog content collection with optional schema fields (description, status, priority, complexity, competitors); added catalog route prerender rule.
AI Intelligence Catalog
content/catalog/ai-intelligence/index.md, content/catalog/ai-intelligence/ai-ranking/index.md, content/catalog/ai-intelligence/local-ai/index.md, content/catalog/ai-intelligence/resume-parsing/index.md
Feature documentation for Glass Box AI ranking, local Ollama integration, and resume parsing with structured data extraction.
Collaboration Catalog
content/catalog/collaboration/index.md, content/catalog/collaboration/candidate-portal/index.md, content/catalog/collaboration/email-notifications/index.md, content/catalog/collaboration/interview-scheduling/index.md, content/catalog/collaboration/team-comments/index.md
Feature documentation for candidate portal, email notifications (BYO-SMTP), interview scheduling, and threaded team comments.
Pipeline Management Catalog
content/catalog/pipeline-management/index.md, content/catalog/pipeline-management/application-tracking/index.md, content/catalog/pipeline-management/candidate-profiles/index.md, content/catalog/pipeline-management/job-management/index.md, content/catalog/pipeline-management/kanban-board/index.md
Feature documentation for applications, candidate profiles, job CRUD with status workflows, and visual Kanban board.
Platform Catalog
content/catalog/platform/index.md, content/catalog/platform/dashboard/index.md, content/catalog/platform/deployment/index.md, content/catalog/platform/multi-tenant/index.md
Feature documentation for recruiter dashboard, one-command Docker Compose deployment, and multi-tenant organization architecture.
Recruitment Tools Catalog
content/catalog/recruitment-tools/index.md, content/catalog/recruitment-tools/custom-application-forms/index.md, content/catalog/recruitment-tools/document-storage/index.md, content/catalog/recruitment-tools/public-job-board/index.md
Feature documentation for public job board with SEO schema, custom application forms with nine field types, and secure MinIO-backed document storage.
Security & Compliance Catalog
content/catalog/security-compliance/index.md, content/catalog/security-compliance/gdpr/index.md, content/catalog/security-compliance/rate-limiting/index.md, content/catalog/security-compliance/server-proxied-documents/index.md
Feature documentation for GDPR data management toolkit, API rate limiting per-IP and per-user, and server-proxied document access without presigned URLs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A catalog blooms with features galore,
Hierarchies branch from the forest floor,
Each sidebar whispers what lies in store,
Giscus joins in with a thoughtful roar—
Documentation gathered, the roadmap's encore! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description lists comprehensive feature additions but lacks required template sections. The 'Summary' and 'Type of change' sections are incomplete or unchecked, and validation/DCO checkboxes remain unmarked. Complete the 'What does this PR change?' and 'Why is this needed?' summary fields, select the appropriate 'Type of change' checkbox (likely 'Feature' or 'Docs'), and verify/complete the Validation and DCO checklist items.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main changes in the PR, which adds multiple features and documentation across AI intelligence, collaboration, pipeline management, recruitment tools, and security compliance categories.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/feature-catalog

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (2)
content/catalog/security-compliance/rate-limiting/index.md (1)

14-19: Clarify “global” scope for distributed deployments.
Line 14 + Line 18 currently read as globally enforced limits, but an in-memory limiter is generally per-instance. Recommend documenting that nuance (or noting Redis/shared-store requirement for truly global enforcement).

💡 Suggested doc patch
-Global per-IP rate limiting on all `/api` endpoints with stricter thresholds for authentication and write operations.
+Per-instance per-IP rate limiting on all `/api` endpoints with stricter thresholds for authentication and write operations.

-- In-memory sliding window rate limiter (`server/utils/rateLimit.ts`)
+- In-memory sliding window rate limiter (`server/utils/rateLimit.ts`)
+- For horizontally scaled deployments, use a shared backend (e.g., Redis) to enforce truly global limits
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/security-compliance/rate-limiting/index.md` around lines 14 -
19, The docs claim "Global per-IP rate limiting" but the implementation
referenced (server/utils/rateLimit.ts) is an in-memory sliding-window limiter
and therefore enforces limits per server instance; update the text in index.md
to clarify that the current middleware is per-instance and not globally enforced
across distributed deployments, and add a note that to achieve true global
enforcement you must replace or back the limiter with a shared store (e.g.,
Redis or another centralized rate-limit store) and briefly indicate that this
change affects the middleware applied to all API routes.
content/catalog/collaboration/candidate-portal/index.md (1)

27-29: Add explicit security requirements for the magic-link flow.

Line 29 currently says “secure link” but doesn’t define core controls. Please specify one-time tokens, short expiration, and invalidation-after-use to avoid weak downstream implementations.

Suggested doc tweak
 ## Design notes

-The portal would use a magic link authentication flow — candidates receive an email with a secure link, no password required. This keeps the experience frictionless and avoids managing another set of credentials.
+The portal would use a magic link authentication flow — candidates receive an email with a secure link, no password required.
+Security requirements: links must be one-time use, short-lived (for example 10–15 minutes), and invalidated immediately after successful authentication.
+This keeps the experience frictionless while maintaining strong account protection.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/collaboration/candidate-portal/index.md` around lines 27 -
29, Update the "magic link authentication flow" description under "Design notes"
to specify concrete security controls: require one-time, single-use tokens;
enforce short expiration (e.g., 10–15 minutes) for the magic link; ensure token
invalidation immediately after successful use; bind tokens to the recipient’s
email and originating IP/user-agent where feasible; and mandate
signed/cryptographically random tokens with secure storage and audit logging.
Reference the "magic link authentication flow" and "Design notes" sections when
adding these requirements.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/GiscusComments.vue`:
- Around line 23-25: The GiscusComments.vue currently sets required attributes
data-repo-id and data-category-id to empty strings which prevents widget
initialization; update the component so script.setAttribute('data-repo-id', ...)
and script.setAttribute('data-category-id', ...) use actual identifiers (e.g.,
passed props, a site config object, or environment variables) instead of empty
literals, or omit setting them only if valid non-empty values are present;
locate the attribute assignments in GiscusComments.vue (the script.setAttribute
calls) and replace the empty-string values with the correct source and add a
guard to avoid injecting empty identifiers.

In `@app/pages/catalog/index.vue`:
- Around line 507-510: The external links opened with target="_blank" in the
catalog template (notably the anchor with href "https://giscus.app" and the
other anchor elements around the same block that use target="_blank") are
missing rel attributes; update each <a> element that has target="_blank" to
include rel="noopener noreferrer" to protect against tabnabbing and preserve
security when opening new tabs. Ensure every anchor in this template that sets
target="_blank" is modified to include rel="noopener noreferrer".
- Around line 431-436: The close icon button is missing an accessible name for
screen readers; update the button element that invokes closeSidebar and renders
the X component to provide an accessible label by adding an aria-label (e.g.,
aria-label="Close sidebar") or an aria-labelledby or visually-hidden text inside
the button so assistive tech can announce its purpose; ensure you modify the
button that uses the X component and the `@click`="closeSidebar" handler so the
label matches the action.
- Around line 423-430: The sidebar panel rendered when sidebarOpen &&
selectedNode must be given dialog semantics and managed focus: add role="dialog"
aria-modal="true" tabindex="-1" on the panel element and set aria-labelledby to
the h3 heading (give the h3 a stable id referencing selectedNode.title); when
the panel opens (watch sidebarOpen/selectedNode) move focus into the panel
(preferably to the close button element) and when it closes restore focus to the
element that opened the sidebar; also implement keyboard handling for Escape to
close and a focus-trap (or use a focus-trap utility) so keyboard focus cannot
escape the panel while open—update the panel div, the h3, the close button, and
the open/close logic to wire these behaviors.

In `@content/catalog/ai-intelligence/ai-ranking/index.md`:
- Line 36: Replace the absolute claim found in the table cell string "| EU AI
Act compliant | ✅ By design | ⚠️ Unclear |" with a softened statement such as "|
EU AI Act compliance | Designed to support compliance (subject to legal review)
| ⚠️ Unclear |" and add a short legal caveat sentence near that section (e.g.,
"Actual compliance depends on deployment context and should be confirmed through
formal legal assessment.") to ensure the page uses cautious, reviewable
language.

In `@content/catalog/ai-intelligence/index.md`:
- Line 8: Update the absolute compliance claim "Applirank eliminates that risk"
to a risk-mitigation phrasing such as "Applirank helps mitigate that risk" or
"Applirank can reduce that risk" and, if appropriate, add a qualifier like "to
the extent practicable" or "when used as recommended" so the statement is not
presented as a legal guarantee; locate the sentence containing the exact phrase
"Applirank eliminates that risk" in the content for ai-intelligence and replace
it with one of the suggested, qualified alternatives.

In `@content/catalog/ai-intelligence/local-ai/index.md`:
- Around line 14-30: The doc makes absolute privacy and market-exclusivity
claims that contradict the planned behavior; update the "Run AI models
locally..." blurb and "Why this is unique" paragraph to remove absolutes (e.g.,
replace "never leaves your network" with "can be configured to keep data on your
network" and note that cloud fallback is available), and tone down exclusivity
in "Why this is unique" by changing "the only ATS" to a defensible
differentiation (e.g., "one of the few ATSs offering configurable local AI
inference"); ensure the "Planned implementation" section explicitly states cloud
fallback is optional and configurable so the public-facing copy aligns with
implemented behavior.

In `@content/catalog/collaboration/interview-scheduling/index.md`:
- Line 20: Fix the malformed markdown heading by separating the merged heading
and paragraph: change "## Considerationsthis" to "## Considerations" and ensure
there is a blank line (newline) after the heading so the following sentence
"this is a complex feature with many edge cases (timezone handling, calendar
sync, rescheduling, panel interviews)..." becomes a proper paragraph; update the
sentence capitalization to "This is a complex feature..." if needed so the
content reads correctly under the "## Considerations" heading.

In `@content/catalog/platform/index.md`:
- Line 8: Replace the specific price mention in the sentence "Applirank runs on
your infrastructure — whether that's a $5/month VPS, a corporate Kubernetes
cluster, or Docker Desktop on your laptop." with a vendor-neutral phrase such as
"a low-cost VPS" or "an entry-level cloud instance" so the documentation doesn't
become stale; update the sentence to read something like "Applirank runs on your
infrastructure — whether that's a low-cost VPS, a corporate Kubernetes cluster,
or Docker Desktop on your laptop."

In `@content/catalog/recruitment-tools/document-storage/index.md`:
- Line 33: The phrase "zero risk of URL leakage" in the documentation (the
paragraph describing MinIO storage and the Nitro server streaming via GET
/api/documents/:id/download and GET /api/documents/:id/preview and the S3
storage key being stripped) overstates security; revise the sentence to use
risk-reduction language (e.g., "significantly reduces the risk of URL leakage"
or "minimizes the risk") and make it clear this is a deliberate trade-off rather
than an absolute guarantee so the S3 storage key stripping is framed as
mitigation not elimination of risk.

In `@content/catalog/recruitment-tools/index.md`:
- Line 6: The user-facing copy in content/catalog/recruitment-tools/index.md
hard-codes "nine field types", which can go out of date; update the sentence in
the Recruitment Tools paragraph to use a non-specific qualifier (e.g., replace
"nine field types" with "multiple field types" or "a variety of field types") or
change it to reference a maintained source (e.g., "see our documentation for
supported field types") so it doesn't become stale; edit the string in that file
where the paragraph describing Applirank's public job board and customizable
application forms appears to remove the specific number.

In `@content/catalog/recruitment-tools/public-job-board/index.md`:
- Line 32: The docs currently hard-code an ISR TTL ("isr: 3600") which can drift
from the real setting; update the public-job-board/index.md to either reference
the authoritative nuxt.config.ts setting (link to or mention the config key
where ISR TTL is defined) or replace the specific value with a qualitative
statement like "ISR-cached with a short TTL" and remove the literal "isr: 3600"
text so the doc no longer claims a specific numeric TTL that may diverge from
the actual nuxt.config.ts configuration.

In `@content/catalog/security-compliance/gdpr/index.md`:
- Line 23: The current "Right to deletion" statement incorrectly implies erasure
across all organizations; update the wording at the "Right to deletion" line so
deletion is scoped to the requesting tenant/controller (e.g., "Remove all
candidate data for the requesting tenant/controller, including tenant-scoped S3
documents"), and add a note that cross-tenant deletions are only allowed when an
explicit lawful basis and documented cross-tenant identity linkage policy and
authorization exist; also require deletion operations to validate
tenant/controller identity, limit S3 object targets to tenant-specific
buckets/prefixes, and record an audit trail of the deletion request and outcome.

In `@content/catalog/security-compliance/index.md`:
- Around line 6-8: Update the copy to avoid absolute guarantees by qualifying
the two categorical phrases: replace "input validation on every endpoint" with a
qualified variant like "input validation on all current endpoints" (or "input
validation on our current endpoints and new endpoints by default where
applicable") and change "Every security feature is on by default" to "Security
features are enabled by default" or "Most security features are enabled by
default" as appropriate; ensure both replacements preserve the intent but use
non-absolute language so the lines referencing input validation and default
security settings are not categorical.

---

Nitpick comments:
In `@content/catalog/collaboration/candidate-portal/index.md`:
- Around line 27-29: Update the "magic link authentication flow" description
under "Design notes" to specify concrete security controls: require one-time,
single-use tokens; enforce short expiration (e.g., 10–15 minutes) for the magic
link; ensure token invalidation immediately after successful use; bind tokens to
the recipient’s email and originating IP/user-agent where feasible; and mandate
signed/cryptographically random tokens with secure storage and audit logging.
Reference the "magic link authentication flow" and "Design notes" sections when
adding these requirements.

In `@content/catalog/security-compliance/rate-limiting/index.md`:
- Around line 14-19: The docs claim "Global per-IP rate limiting" but the
implementation referenced (server/utils/rateLimit.ts) is an in-memory
sliding-window limiter and therefore enforces limits per server instance; update
the text in index.md to clarify that the current middleware is per-instance and
not globally enforced across distributed deployments, and add a note that to
achieve true global enforcement you must replace or back the limiter with a
shared store (e.g., Redis or another centralized rate-limit store) and briefly
indicate that this change affects the middleware applied to all API routes.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1483cbc and e53572c.

📒 Files selected for processing (34)
  • app/components/GiscusComments.vue
  • app/pages/blog/[...slug].vue
  • app/pages/blog/index.vue
  • app/pages/catalog/index.vue
  • app/pages/index.vue
  • app/pages/roadmap.vue
  • content.config.ts
  • content/catalog/ai-intelligence/ai-ranking/index.md
  • content/catalog/ai-intelligence/index.md
  • content/catalog/ai-intelligence/local-ai/index.md
  • content/catalog/ai-intelligence/resume-parsing/index.md
  • content/catalog/collaboration/candidate-portal/index.md
  • content/catalog/collaboration/email-notifications/index.md
  • content/catalog/collaboration/index.md
  • content/catalog/collaboration/interview-scheduling/index.md
  • content/catalog/collaboration/team-comments/index.md
  • content/catalog/pipeline-management/application-tracking/index.md
  • content/catalog/pipeline-management/candidate-profiles/index.md
  • content/catalog/pipeline-management/index.md
  • content/catalog/pipeline-management/job-management/index.md
  • content/catalog/pipeline-management/kanban-board/index.md
  • content/catalog/platform/dashboard/index.md
  • content/catalog/platform/deployment/index.md
  • content/catalog/platform/index.md
  • content/catalog/platform/multi-tenant/index.md
  • content/catalog/recruitment-tools/custom-application-forms/index.md
  • content/catalog/recruitment-tools/document-storage/index.md
  • content/catalog/recruitment-tools/index.md
  • content/catalog/recruitment-tools/public-job-board/index.md
  • content/catalog/security-compliance/gdpr/index.md
  • content/catalog/security-compliance/index.md
  • content/catalog/security-compliance/rate-limiting/index.md
  • content/catalog/security-compliance/server-proxied-documents/index.md
  • nuxt.config.ts

Comment on lines +23 to +25
script.setAttribute('data-repo-id', '')
script.setAttribute('data-category', 'Feature Catalog')
script.setAttribute('data-category-id', '')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Giscus configuration data-repo-id data-category-id required parameters official documentation

💡 Result:

In the official giscus embed snippet, data-repo-id and data-category-id are part of the required core identification fields—along with:

  • data-repo (the owner/repo string)
  • data-repo-id (the repository “node id” shown by giscus)
  • data-category (discussion category name)
  • data-category-id (discussion category “node id” shown by giscus)

These are the values giscus uses to connect your site to the correct GitHub Discussions repo/category; the official configurator generates them in the “Enable giscus” <script> tag. [1]

Official documentation: the giscus configuration page (“Enable giscus” section). [1]

Sources

  • [1] giscus configuration page (embed snippet showing data-repo, data-repo-id, data-category, data-category-id): https://giscus.app/en

Giscus widget will fail to initialize with empty data-repo-id and data-category-id.

Lines 23 and 25 set required Giscus identifiers to empty strings. According to official Giscus documentation, data-repo-id and data-category-id are core identification fields required to connect your site to the correct GitHub Discussions repo and category. Empty values will prevent the widget from loading.

🔧 Suggested fix
 const containerRef = ref<HTMLDivElement>()
+const runtimeConfig = useRuntimeConfig()

 function loadGiscus() {
   if (!containerRef.value) return

   // Clear any existing widget
   containerRef.value.innerHTML = ''

+  const repo = runtimeConfig.public.giscusRepo || 'applirank/applirank'
+  const repoId = runtimeConfig.public.giscusRepoId
+  const category = runtimeConfig.public.giscusCategory || 'Feature Catalog'
+  const categoryId = runtimeConfig.public.giscusCategoryId
+
+  if (!repoId || !categoryId) {
+    console.error('Missing Giscus config: giscusRepoId / giscusCategoryId')
+    return
+  }
+
   const script = document.createElement('script')
   script.src = 'https://giscus.app/client.js'
-  script.setAttribute('data-repo', 'applirank/applirank')
-  script.setAttribute('data-repo-id', '')
-  script.setAttribute('data-category', 'Feature Catalog')
-  script.setAttribute('data-category-id', '')
+  script.setAttribute('data-repo', repo)
+  script.setAttribute('data-repo-id', repoId)
+  script.setAttribute('data-category', category)
+  script.setAttribute('data-category-id', categoryId)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/GiscusComments.vue` around lines 23 - 25, The
GiscusComments.vue currently sets required attributes data-repo-id and
data-category-id to empty strings which prevents widget initialization; update
the component so script.setAttribute('data-repo-id', ...) and
script.setAttribute('data-category-id', ...) use actual identifiers (e.g.,
passed props, a site config object, or environment variables) instead of empty
literals, or omit setting them only if valid non-empty values are present;
locate the attribute assignments in GiscusComments.vue (the script.setAttribute
calls) and replace the empty-string values with the correct source and add a
guard to avoid injecting empty identifiers.

Comment on lines +423 to +430
v-if="sidebarOpen && selectedNode"
class="fixed top-14 right-0 bottom-0 z-50 w-full max-w-md overflow-y-auto border-l border-white/[0.06] bg-[#0d0d0f] lg:z-30"
>
<!-- Close button -->
<div class="sticky top-0 z-10 flex items-center justify-between border-b border-white/[0.06] bg-[#0d0d0f]/95 backdrop-blur-sm px-5 py-3">
<h3 class="text-[15px] font-semibold text-white truncate pr-4">
{{ selectedNode.title }}
</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Sidebar should expose dialog semantics and manage focus.

At Line 423, this behaves like a modal panel but lacks dialog semantics and focus entry point, so keyboard/screen-reader navigation can escape behind the panel.

♿ Suggested patch (baseline semantics)
- <div
-   v-if="sidebarOpen && selectedNode"
-   class="fixed top-14 right-0 bottom-0 z-50 w-full max-w-md overflow-y-auto border-l border-white/[0.06] bg-[`#0d0d0f`] lg:z-30"
- >
+ <div
+   v-if="sidebarOpen && selectedNode"
+   class="fixed top-14 right-0 bottom-0 z-50 w-full max-w-md overflow-y-auto border-l border-white/[0.06] bg-[`#0d0d0f`] lg:z-30"
+   role="dialog"
+   aria-modal="true"
+   aria-labelledby="catalog-sidebar-title"
+   tabindex="-1"
+ >
...
- <h3 class="text-[15px] font-semibold text-white truncate pr-4">
+ <h3 id="catalog-sidebar-title" class="text-[15px] font-semibold text-white truncate pr-4">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
v-if="sidebarOpen && selectedNode"
class="fixed top-14 right-0 bottom-0 z-50 w-full max-w-md overflow-y-auto border-l border-white/[0.06] bg-[#0d0d0f] lg:z-30"
>
<!-- Close button -->
<div class="sticky top-0 z-10 flex items-center justify-between border-b border-white/[0.06] bg-[#0d0d0f]/95 backdrop-blur-sm px-5 py-3">
<h3 class="text-[15px] font-semibold text-white truncate pr-4">
{{ selectedNode.title }}
</h3>
v-if="sidebarOpen && selectedNode"
class="fixed top-14 right-0 bottom-0 z-50 w-full max-w-md overflow-y-auto border-l border-white/[0.06] bg-[`#0d0d0f`] lg:z-30"
role="dialog"
aria-modal="true"
aria-labelledby="catalog-sidebar-title"
tabindex="-1"
>
<!-- Close button -->
<div class="sticky top-0 z-10 flex items-center justify-between border-b border-white/[0.06] bg-[`#0d0d0f`]/95 backdrop-blur-sm px-5 py-3">
<h3 id="catalog-sidebar-title" class="text-[15px] font-semibold text-white truncate pr-4">
{{ selectedNode.title }}
</h3>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/catalog/index.vue` around lines 423 - 430, The sidebar panel
rendered when sidebarOpen && selectedNode must be given dialog semantics and
managed focus: add role="dialog" aria-modal="true" tabindex="-1" on the panel
element and set aria-labelledby to the h3 heading (give the h3 a stable id
referencing selectedNode.title); when the panel opens (watch
sidebarOpen/selectedNode) move focus into the panel (preferably to the close
button element) and when it closes restore focus to the element that opened the
sidebar; also implement keyboard handling for Escape to close and a focus-trap
(or use a focus-trap utility) so keyboard focus cannot escape the panel while
open—update the panel div, the h3, the close button, and the open/close logic to
wire these behaviors.

Comment on lines +431 to +436
<button
class="rounded-md p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white"
@click="closeSidebar"
>
<X class="size-4" />
</button>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Close button needs an accessible name.

At Line 431, the icon-only button has no label for assistive technologies.

♿ Suggested patch
- <button
-   class="rounded-md p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white"
-   `@click`="closeSidebar"
- >
+ <button
+   type="button"
+   aria-label="Close feature details"
+   class="rounded-md p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white"
+   `@click`="closeSidebar"
+ >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
class="rounded-md p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white"
@click="closeSidebar"
>
<X class="size-4" />
</button>
<button
type="button"
aria-label="Close feature details"
class="rounded-md p-1.5 text-white/40 transition hover:bg-white/[0.06] hover:text-white"
`@click`="closeSidebar"
>
<X class="size-4" />
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/catalog/index.vue` around lines 431 - 436, The close icon button is
missing an accessible name for screen readers; update the button element that
invokes closeSidebar and renders the X component to provide an accessible label
by adding an aria-label (e.g., aria-label="Close sidebar") or an aria-labelledby
or visually-hidden text inside the button so assistive tech can announce its
purpose; ensure you modify the button that uses the X component and the
`@click`="closeSidebar" handler so the label matches the action.

Comment on lines +507 to +510
<p class="text-[12px] text-white/30 mb-3">
Sign in with GitHub to comment. Discussions are powered by
<a href="https://giscus.app" target="_blank" class="text-brand-400 hover:underline">Giscus</a>.
</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add rel="noopener noreferrer" to external _blank links.

Line 509 and Line 532 open new tabs without rel, which weakens tabnabbing protections.

🔒 Suggested patch
- <a href="https://giscus.app" target="_blank" class="text-brand-400 hover:underline">Giscus</a>.
+ <a href="https://giscus.app" target="_blank" rel="noopener noreferrer" class="text-brand-400 hover:underline">Giscus</a>.

- <a
-   href="https://github.com/applirank/applirank"
-   target="_blank"
-   class="inline-flex items-center gap-1 text-white/50 hover:text-white transition"
- >
+ <a
+   href="https://github.com/applirank/applirank"
+   target="_blank"
+   rel="noopener noreferrer"
+   class="inline-flex items-center gap-1 text-white/50 hover:text-white transition"
+ >

Also applies to: 530-533

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/pages/catalog/index.vue` around lines 507 - 510, The external links
opened with target="_blank" in the catalog template (notably the anchor with
href "https://giscus.app" and the other anchor elements around the same block
that use target="_blank") are missing rel attributes; update each <a> element
that has target="_blank" to include rel="noopener noreferrer" to protect against
tabnabbing and preserve security when opening new tabs. Ensure every anchor in
this template that sets target="_blank" is modified to include rel="noopener
noreferrer".

| Reasoning shown | ✅ Per-criterion breakdown | ❌ "AI confidence score" |
| Criteria editable | ✅ Per-job configuration | ❌ Fixed algorithm |
| Bias auditable | ✅ Full logic exposed | ❌ Trust the vendor |
| EU AI Act compliant | ✅ By design | ⚠️ Unclear |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid absolute regulatory compliance claims in planned docs.

Line 36 claims EU AI Act compliance “by design,” but this is a legal assertion that typically requires formal assessment and may change by deployment context. Please soften this to “designed to support compliance” plus legal review language.

Suggested wording
-| EU AI Act compliant | ✅ By design | ⚠️ Unclear |
+| EU AI Act alignment | ✅ Designed to support compliance (subject to legal review) | ⚠️ Unclear |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| EU AI Act compliant |By design | ⚠️ Unclear |
| EU AI Act alignment |Designed to support compliance (subject to legal review) | ⚠️ Unclear |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/ai-intelligence/ai-ranking/index.md` at line 36, Replace the
absolute claim found in the table cell string "| EU AI Act compliant | ✅ By
design | ⚠️ Unclear |" with a softened statement such as "| EU AI Act compliance
| Designed to support compliance (subject to legal review) | ⚠️ Unclear |" and
add a short legal caveat sentence near that section (e.g., "Actual compliance
depends on deployment context and should be confirmed through formal legal
assessment.") to ensure the page uses cautious, reviewable language.


## Security model

Documents are stored in a private MinIO bucket. The Nitro server streams files to authenticated users via `GET /api/documents/:id/download` and `GET /api/documents/:id/preview`. The S3 storage key is stripped from all API responses, so clients never learn the internal path. This is a deliberate trade-off: slightly more server load in exchange for zero risk of URL leakage.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid absolute security guarantees (“zero risk”).

Line 33 states “zero risk of URL leakage,” which over-claims security posture. Prefer risk-reduction language to avoid compliance/audit friction.

Suggested wording
-... This is a deliberate trade-off: slightly more server load in exchange for zero risk of URL leakage.
+... This is a deliberate trade-off: slightly more server load in exchange for significantly reduced URL leakage risk.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Documents are stored in a private MinIO bucket. The Nitro server streams files to authenticated users via `GET /api/documents/:id/download` and `GET /api/documents/:id/preview`. The S3 storage key is stripped from all API responses, so clients never learn the internal path. This is a deliberate trade-off: slightly more server load in exchange for zero risk of URL leakage.
Documents are stored in a private MinIO bucket. The Nitro server streams files to authenticated users via `GET /api/documents/:id/download` and `GET /api/documents/:id/preview`. The S3 storage key is stripped from all API responses, so clients never learn the internal path. This is a deliberate trade-off: slightly more server load in exchange for significantly reduced URL leakage risk.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/recruitment-tools/document-storage/index.md` at line 33, The
phrase "zero risk of URL leakage" in the documentation (the paragraph describing
MinIO storage and the Nitro server streaming via GET /api/documents/:id/download
and GET /api/documents/:id/preview and the S3 storage key being stripped)
overstates security; revise the sentence to use risk-reduction language (e.g.,
"significantly reduces the risk of URL leakage" or "minimizes the risk") and
make it clear this is a deliberate trade-off rather than an absolute guarantee
so the S3 storage key stripping is framed as mitigation not elimination of risk.

description: "Public-facing pages and tools that connect your hiring pipeline to the outside world — job boards, application forms, and document management."
---

Recruitment Tools are the outward-facing surface of your ATS. They're what candidates see and interact with. Applirank provides a public job board with SEO-optimized pages, customizable application forms with nine field types, and secure document management backed by S3-compatible storage.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hard-coded field count risks going stale.

"nine field types" is a specific number embedded in user-facing copy. If the set of supported field types changes, this sentence will silently contradict the actual product. Consider replacing with a vaguer qualifier (e.g., "multiple field types") or ensure this line is updated as part of any custom-application-forms changelog.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/recruitment-tools/index.md` at line 6, The user-facing copy
in content/catalog/recruitment-tools/index.md hard-codes "nine field types",
which can go out of date; update the sentence in the Recruitment Tools paragraph
to use a non-specific qualifier (e.g., replace "nine field types" with "multiple
field types" or "a variety of field types") or change it to reference a
maintained source (e.g., "see our documentation for supported field types") so
it doesn't become stale; edit the string in that file where the paragraph
describing Applirank's public job board and customizable application forms
appears to remove the specific number.


## Design notes

Pages are ISR-cached with a 1-hour TTL (`isr: 3600`) so they serve instantly while staying reasonably fresh. The public API intentionally exposes only the fields candidates need — no internal notes, scores, or pipeline data leak.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hard-coded ISR TTL in docs will drift from the actual config.

isr: 3600 is a specific implementation value. If the TTL is later tuned in nuxt.config.ts, this doc will describe the wrong caching behaviour. Either link to the config source or keep the description qualitative (e.g., "ISR-cached with a short TTL").

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/recruitment-tools/public-job-board/index.md` at line 32, The
docs currently hard-code an ISR TTL ("isr: 3600") which can drift from the real
setting; update the public-job-board/index.md to either reference the
authoritative nuxt.config.ts setting (link to or mention the config key where
ISR TTL is defined) or replace the specific value with a qualitative statement
like "ISR-cached with a short TTL" and remove the literal "isr: 3600" text so
the doc no longer claims a specific numeric TTL that may diverge from the actual
nuxt.config.ts configuration.

## Planned implementation

- **Data export** — Generate a JSON/CSV bundle of all data associated with a candidate (profile, applications, documents, question responses)
- **Right to deletion** — Remove all candidate data across all organizations, including S3 documents

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Scope deletion to the requesting tenant/controller, not all organizations.
Line 23 creates a major compliance risk in multi-tenant setups. Erasure should be tenant-scoped unless there is an explicit lawful basis and cross-tenant identity linkage policy.

🛠️ Suggested doc correction
-- **Right to deletion** — Remove all candidate data across all organizations, including S3 documents
+- **Right to deletion** — Remove all candidate data for the requesting organization (tenant), including S3 documents, with policy-controlled handling for any cross-tenant identity matches
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- **Right to deletion** — Remove all candidate data across all organizations, including S3 documents
- **Right to deletion** — Remove all candidate data for the requesting organization (tenant), including S3 documents, with policy-controlled handling for any cross-tenant identity matches
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/security-compliance/gdpr/index.md` at line 23, The current
"Right to deletion" statement incorrectly implies erasure across all
organizations; update the wording at the "Right to deletion" line so deletion is
scoped to the requesting tenant/controller (e.g., "Remove all candidate data for
the requesting tenant/controller, including tenant-scoped S3 documents"), and
add a note that cross-tenant deletions are only allowed when an explicit lawful
basis and documented cross-tenant identity linkage policy and authorization
exist; also require deletion operations to validate tenant/controller identity,
limit S3 object targets to tenant-specific buckets/prefixes, and record an audit
trail of the deletion request and outcome.

Comment on lines +6 to +8
Security in an ATS isn't optional — you're handling people's personal data, resumes, and employment history. Applirank ships with production-ready security defaults: API rate limiting, comprehensive security headers, server-proxied document access, and input validation on every endpoint.

Every security feature is on by default. You don't have to remember to enable it.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Absolute security guarantees in public copy are high-maintenance.

"input validation on every endpoint" and "Every security feature is on by default" are categorical claims. A single future endpoint added without validation, or any opt-in security setting, will silently contradict this copy. Consider qualifying these (e.g., "all current endpoints", "security features are enabled by default") to reduce the maintenance burden as the product evolves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@content/catalog/security-compliance/index.md` around lines 6 - 8, Update the
copy to avoid absolute guarantees by qualifying the two categorical phrases:
replace "input validation on every endpoint" with a qualified variant like
"input validation on all current endpoints" (or "input validation on our current
endpoints and new endpoints by default where applicable") and change "Every
security feature is on by default" to "Security features are enabled by default"
or "Most security features are enabled by default" as appropriate; ensure both
replacements preserve the intent but use non-absolute language so the lines
referencing input validation and default security settings are not categorical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant