Skip to content

Sync feature/nestbot-ai-assistant with main - #4549

Merged
arkid15r merged 19 commits into
OWASP:feature/nestbot-ai-assistantfrom
rudransh-shrivastava:feature/nestbot-ai-assistant
Apr 13, 2026
Merged

arkid15r merged 19 commits into
OWASP:feature/nestbot-ai-assistantfrom
rudransh-shrivastava:feature/nestbot-ai-assistant

Conversation

@rudransh-shrivastava

Copy link
Copy Markdown
Collaborator

Proposed change

Sync feature/nestbot-ai-assistant with main

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

dependabot Bot and others added 19 commits April 8, 2026 21:27
Bumps the npm_and_yarn group with 1 update in the /frontend directory: [basic-ftp](https://github.com/patrickjuchli/basic-ftp).


Updates `basic-ftp` from 5.2.0 to 5.2.1
- [Release notes](https://github.com/patrickjuchli/basic-ftp/releases)
- [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md)
- [Commits](patrickjuchli/basic-ftp@v5.2.0...v5.2.1)

---
updated-dependencies:
- dependency-name: basic-ftp
  dependency-version: 5.2.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ASP#4522)

Bumps the pip group with 2 updates in the /backend directory: [cryptography](https://github.com/pyca/cryptography) and [langchain-core](https://github.com/langchain-ai/langchain).


Updates `cryptography` from 46.0.6 to 46.0.7
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](pyca/cryptography@46.0.6...46.0.7)

Updates `langchain-core` from 1.2.23 to 1.2.28
- [Release notes](https://github.com/langchain-ai/langchain/releases)
- [Commits](langchain-ai/langchain@langchain-core==1.2.23...langchain-core==1.2.28)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 46.0.7
  dependency-type: indirect
  dependency-group: pip
- dependency-name: langchain-core
  dependency-version: 1.2.28
  dependency-type: indirect
  dependency-group: pip
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat: add Playwright smoke test for board candidates page

* feat: improve board candidates smoke test assertions

* Update code

---------

Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com>
Bumps the npm_and_yarn group with 1 update in the /frontend directory: [basic-ftp](https://github.com/patrickjuchli/basic-ftp).


Updates `basic-ftp` from 5.2.1 to 5.2.2
- [Release notes](https://github.com/patrickjuchli/basic-ftp/releases)
- [Changelog](https://github.com/patrickjuchli/basic-ftp/blob/master/CHANGELOG.md)
- [Commits](patrickjuchli/basic-ftp@v5.2.1...v5.2.2)

---
updated-dependencies:
- dependency-name: basic-ftp
  dependency-version: 5.2.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* ci: add pre-commit ecosystem support for Dependabot

* Update code

---------

Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
* docs: add local testing steps to NestBot development guide

Closes OWASP#4333

## Summary
Adds missing local testing instructions to the NestBot 
Development section in CONTRIBUTING.md.

## Changes
- Added Testing NestBot Locally subsection
- Included make logs command for checking Django events

## Checklist
- [x] Documentation only change
- [x] No new dependencies added
- [x] Improves contributor onboarding experience

* Update code

---------

Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
…Guidelines (OWASP#4508)

Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com>
* fix:add-ecs-containers-health

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* fix: add frontend health endpoint

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* bot suggestion

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* Update fuzz-compose.yml

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* edit endpoint to /api/health

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* Update ALB variables.tf

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* changes after: make check run

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>

* update code

---------

Signed-off-by: hassaansaleem28 <iamhassaans@gmail.com>
Co-authored-by: Rudransh Shrivastava <rudransh.shrivastava@owasp.org>
Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com>
* fix: remove timeline rendering tests and related code from Card component

* resolved comments

---------

Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com>
* docs: add local testing steps for NestBot

* fix: markdown lint issues in NestBot testing section

* fix: Remove trailing whitespace at Line 278

---------

Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
Bumps [eslint](https://github.com/eslint/eslint) in `/frontend` from 10.0.3 to 10.1.0.


Updates `eslint` from 10.0.3 to 10.1.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.0.3...v10.1.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
 OWASP#4527) (OWASP#4530)

Co-authored-by: Deepak Singh <deepak.singh@example.com>
Co-authored-by: Arkadii Yakovets <2201626+arkid15r@users.noreply.github.com>
* Improve navbar hover and active states

* Fix aria-current accessibility issue

* Remove unintended lockfile changes

* Update code

---------

Co-authored-by: Kate <kate@kgthreads.com>
…WASP#3608)

* feat: Enable Pagination for Pull Requests on Issue and Module Pages

* fix: code rabbit address

* fix: code rabbit comments

* fix:cubic-dev-ai comment

* fix: cubic-dev-ai error

* fix: cubic-dev-ai comments

* fix: cubic-dev-ai error

* fix cubic-dev-ai error

* Update code

---------

Co-authored-by: Kate <kate@kgthreads.com>
…gation (OWASP#4539)

* fix: remove community from HIDDEN_SEGMENTS to restore breadcrumb navigation

* Update code

---------

Co-authored-by: Arkadii Yakovets <arkadii.yakovets@owasp.org>
@coderabbitai

coderabbitai Bot commented Apr 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

Release Notes

  • New Features

    • Added paginated pull request lists with "Show more" and "Show less" controls throughout the app
    • Added Community segment visibility in navigation breadcrumbs
    • Added health check endpoint for service monitoring
  • Bug Fixes

    • Fixed Slack invite link baseline reset logic when detecting new invite commits
    • Updated service health check monitoring endpoints
  • Documentation

    • Enhanced local development setup instructions for NestBot configuration
  • Tests

    • Expanded test coverage for pagination, UI components, and page rendering
  • Chores

    • Updated dependencies and pre-commit hook versions

Walkthrough

This PR implements pull-request pagination across the stack: backend GraphQL resolvers now support limit and offset parameters for pull_requests and recent_pull_requests fields; frontend pages use Apollo's fetchMore to incrementally load more PRs with pagination UI controls; infrastructure health-check endpoints are updated; and supporting tests and type definitions are added.

Changes

Cohort / File(s) Summary
Configuration & Tooling
.github/dependabot.yml, .pre-commit-config.yaml, frontend/package.json
Added Dependabot pre-commit ecosystem config, updated pre-commit hook versions to commit SHAs, and bumped Next.js/ESLint versions.
Backend GraphQL Pagination
backend/apps/github/api/internal/nodes/issue.py, backend/apps/mentorship/api/internal/nodes/module.py
Added limit and offset parameters to pull_requests and recent_pull_requests GraphQL fields with slicing, normalization, and prefetch_related logic.
Backend Tests
backend/tests/unit/apps/github/api/internal/nodes/issue_test.py, backend/tests/unit/apps/slack/commands/slack_check_invite_link_test.py
Added unit tests for pagination behavior and baseline reset logic in invite link management.
Slack Command Logic
backend/apps/slack/management/commands/slack_check_invite_link.py
Adjusted baseline update condition to reset when invite commit is updated, even if member count already set.
Documentation
CONTRIBUTING.md
Updated NestBot Slack/ngrok setup instructions and added "Testing NestBot Locally" section with smoke-test guidance.
Frontend GraphQL Queries
frontend/src/server/queries/issueQueries.ts, frontend/src/server/queries/moduleQueries.ts
Updated GET_MODULE_ISSUE_VIEW and GET_PROGRAM_ADMINS_AND_MODULES to accept and pass pagination variables (limit, offset).
Frontend Components
frontend/src/components/CardDetailsPage.tsx, frontend/src/components/Card.tsx, frontend/src/components/Header.tsx, frontend/src/components/LoadingSpinner.tsx
Added pagination callbacks (onLoadMorePullRequests, onResetPullRequests, isFetchingMore) to DetailsCard, removed timeline UI from Card, improved Header hover styling and conditional aria-current, restructured LoadingSpinner DOM layering.
Frontend Pages with Pagination
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx, frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx, frontend/__tests__/unit/pages/PublicMentorshipModulePage.test.tsx
Implemented incremental PR loading with Apollo fetchMore, state management (hasMorePRs, visibleCount, isFetchingMore), and comprehensive pagination tests.
Frontend Type Definitions
frontend/src/types/card.ts, frontend/src/types/mentorship.ts
Extended DetailsCardProps with pagination callbacks and Module type with recentPullRequests and project metadata fields.
Frontend Utilities & Hooks
frontend/src/hooks/useBreadcrumbs.ts, frontend/src/utils/getIcsFileUrl.ts
Removed 'community' from HIDDEN_SEGMENTS to show breadcrumb; updated date parsing to use UTC components.
Frontend Unit Tests
frontend/__tests__/unit/components/CardDetailsPage.test.tsx, frontend/__tests__/unit/components/Card.test.tsx, frontend/__tests__/unit/components/LoadingSpinner.test.tsx, frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx, frontend/__tests__/unit/pages/Header.test.tsx, frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx, frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx, frontend/__tests__/unit/utils/getIcsFileUrl.test.ts, frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx
Added pagination UI tests, updated CardDetailsPage mocks, removed timeline tests, improved mobile menu test scoping, and relaxed date parsing assertions.
Frontend E2E Tests
frontend/__tests__/e2e/pages/BoardCandidates.spec.ts
Added new Playwright test for Board Candidates page with valid/invalid year scenarios and 404 handling.
Frontend Health Endpoint
frontend/src/app/api/health/route.ts
Added new GET route handler returning { status: 'ok' } for health checks.
Docker Health Checks
docker-compose/e2e/compose.yaml, docker-compose/fuzz/compose.yaml
Updated backend and frontend healthcheck endpoints from /a/ and / to /status/ and /api/health.
Infrastructure Health Check Configuration
infrastructure/live/main.tf, infrastructure/modules/alb/variables.tf
Updated ALB frontend health check path default to /api/health and passed health_check_path variables to backend/frontend service modules.
Infrastructure Service Module
infrastructure/modules/service/main.tf, infrastructure/modules/service/variables.tf, infrastructure/modules/service/tests/service.tftest.hcl
Added health_check_path input variable, implemented ECS container healthCheck with wget --spider command, and added tests asserting health check presence and configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

backend, backend-tests, frontend, frontend-tests, infrastructure, ci

Suggested reviewers

  • kasya
  • arkid15r
  • cubic-dev-ai
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Sync feature/nestbot-ai-assistant with main' accurately describes a branch synchronization, which aligns with merging the feature branch into main. However, it doesn't convey the substantial scope of changes (pagination, health checks, infrastructure updates, tests) in this comprehensive PR.
Description check ✅ Passed The description clearly states the intent to sync the feature branch with main and confirms adherence to the contributing workflow, local verification, and test passage. It relates directly to the changeset's nature as a merge/sync operation.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@github-actions

Copy link
Copy Markdown

PR validation failed: No linked issue and no valid closing issue reference in PR description

@codecov

codecov Bot commented Apr 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.07407% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.93%. Comparing base (b13837e) to head (2b5502e).
⚠️ Report is 20 commits behind head on feature/nestbot-ai-assistant.

Files with missing lines Patch % Lines
[...Key]/modules/[moduleKey]/issues/[issueId]/page.tsx](https://app.codecov.io/gh/OWASP/Nest/pull/4549?src=pr&el=tree&filepath=frontend%2Fsrc%2Fapp%2Fmy%2Fmentorship%2Fprograms%2F%5BprogramKey%5D%2Fmodules%2F%5BmoduleKey%5D%2Fissues%2F%5BissueId%5D%2Fpage.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=OWASP#diff-ZnJvbnRlbmQvc3JjL2FwcC9teS9tZW50b3JzaGlwL3Byb2dyYW1zL1twcm9ncmFtS2V5XS9tb2R1bGVzL1ttb2R1bGVLZXldL2lzc3Vlcy9baXNzdWVJZF0vcGFnZS50c3g=) 61.53% 6 Missing and 9 partials ⚠️
...programs/[programKey]/modules/[moduleKey]/page.tsx 80.64% 0 Missing and 6 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@                       Coverage Diff                        @@
##           feature/nestbot-ai-assistant    #4549      +/-   ##
================================================================
- Coverage                         98.96%   98.93%   -0.04%     
================================================================
  Files                               524      527       +3     
  Lines                             16745    16916     +171     
  Branches                           2312     2356      +44     
================================================================
+ Hits                              16572    16736     +164     
+ Misses                              105       97       -8     
- Partials                             68       83      +15     
Flag Coverage Δ
frontend 97.34% <73.75%> (-0.45%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
backend/apps/github/api/internal/nodes/issue.py 100.00% <ø> (ø)
...ckend/apps/mentorship/api/internal/nodes/module.py 100.00% <ø> (ø)
...ack/management/commands/slack_check_invite_link.py 100.00% <100.00%> (ø)
frontend/src/components/Card.tsx 100.00% <ø> (ø)
frontend/src/components/CardDetailsPage.tsx 100.00% <100.00%> (ø)
frontend/src/components/Header.tsx 100.00% <100.00%> (ø)
frontend/src/components/LoadingSpinner.tsx 100.00% <ø> (ø)
...programs/[programKey]/modules/[moduleKey]/page.tsx 89.28% <80.64%> (-10.72%) ⬇️
[...Key]/modules/[moduleKey]/issues/[issueId]/page.tsx](https://app.codecov.io/gh/OWASP/Nest/pull/4549?src=pr&el=tree&filepath=frontend%2Fsrc%2Fapp%2Fmy%2Fmentorship%2Fprograms%2F%5BprogramKey%5D%2Fmodules%2F%5BmoduleKey%5D%2Fissues%2F%5BissueId%5D%2Fpage.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=OWASP#diff-ZnJvbnRlbmQvc3JjL2FwcC9teS9tZW50b3JzaGlwL3Byb2dyYW1zL1twcm9ncmFtS2V5XS9tb2R1bGVzL1ttb2R1bGVLZXldL2lzc3Vlcy9baXNzdWVJZF0vcGFnZS50c3g=) 89.63% <61.53%> (-8.87%) ⬇️

... and 5 files with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update b13837e...2b5502e. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sonarqubecloud

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 45 files

Confidence score: 3/5

  • There is a concrete regression risk: changing the default resolver behavior in backend/apps/github/api/internal/nodes/issue.py to limit=4 alters the field contract and can silently truncate results for callers that omit limit.
  • frontend/src/server/queries/issueQueries.ts introduces default pagination variables that may cause refetches to return only the first 4 pull requests after mutations, which is user-visible and likely confusing.
  • Given the high confidence on both findings and direct impact on list completeness, this carries some merge risk, though it looks fixable with targeted default/variable handling updates.
  • Pay close attention to backend/apps/github/api/internal/nodes/issue.py, frontend/src/server/queries/issueQueries.ts - default pagination values can unintentionally truncate returned issue/PR lists.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="backend/apps/github/api/internal/nodes/issue.py">

<violation number="1" location="backend/apps/github/api/internal/nodes/issue.py:48">
P1: Defaulting this resolver to `limit=4` changes the existing field contract and silently truncates results for callers that do not pass a limit.</violation>
</file>

<file name="frontend/src/server/queries/issueQueries.ts">

<violation number="1" location="frontend/src/server/queries/issueQueries.ts:32">
P2: Adding pagination defaults here collapses the pull-request list back to the first 4 items after mutation refetches, because existing `refetchQueries` callers do not pass the new `limit`/`offset` variables.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +48 to +56
def pull_requests(self, limit: int = 4, offset: int = 0) -> list[PullRequestNode]:
"""Return pull requests linked to this issue."""
if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
return []

offset = max(0, offset)
return list(
self.pull_requests.all().order_by("-created_at")[offset : offset + normalized_limit]
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1: Defaulting this resolver to limit=4 changes the existing field contract and silently truncates results for callers that do not pass a limit.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/apps/github/api/internal/nodes/issue.py, line 48:

<comment>Defaulting this resolver to `limit=4` changes the existing field contract and silently truncates results for callers that do not pass a limit.</comment>

<file context>
@@ -40,7 +43,17 @@ class IssueNode(strawberry.relay.Node):
-    pull_requests: list[PullRequestNode] = strawberry_django.field()
+
+    @strawberry_django.field(prefetch_related=["pull_requests"])
+    def pull_requests(self, limit: int = 4, offset: int = 0) -> list[PullRequestNode]:
+        """Return pull requests linked to this issue."""
+        if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
</file context>
Suggested change
def pull_requests(self, limit: int = 4, offset: int = 0) -> list[PullRequestNode]:
"""Return pull requests linked to this issue."""
if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
return []
offset = max(0, offset)
return list(
self.pull_requests.all().order_by("-created_at")[offset : offset + normalized_limit]
)
def pull_requests(self, limit: int | None = None, offset: int = 0) -> list[PullRequestNode]:
"""Return pull requests linked to this issue."""
offset = max(0, offset)
queryset = self.pull_requests.all().order_by("-created_at")
if limit is None:
return list(queryset[offset:])
if (normalized_limit := normalize_limit(limit, MAX_LIMIT)) is None:
return []
return list(queryset[offset : offset + normalized_limit])

}
labels
pullRequests {
pullRequests(limit: $limit, offset: $offset) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Adding pagination defaults here collapses the pull-request list back to the first 4 items after mutation refetches, because existing refetchQueries callers do not pass the new limit/offset variables.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At frontend/src/server/queries/issueQueries.ts, line 32:

<comment>Adding pagination defaults here collapses the pull-request list back to the first 4 items after mutation refetches, because existing `refetchQueries` callers do not pass the new `limit`/`offset` variables.</comment>

<file context>
@@ -23,7 +29,7 @@ export const GET_MODULE_ISSUE_VIEW = gql`
         }
         labels
-        pullRequests {
+        pullRequests(limit: $limit, offset: $offset) {
           id
           title
</file context>

@rudransh-shrivastava
rudransh-shrivastava marked this pull request as ready for review April 13, 2026 16:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (10)
CONTRIBUTING.md (1)

276-276: Clarify the exact log command for faster local troubleshooting.

Consider adding the specific command to view logs (for example, the project’s make target or container command) right here to reduce ambiguity for first-time contributors.

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

In `@CONTRIBUTING.md` at line 276, Update the line "To confirm Slack is reaching
your machine, check the backend container logs." to include exact commands for
viewing those logs: add the project's make target name (e.g., the make target
used for backend logs) and the container orchestration command (e.g., the
docker-compose or docker command and the flag to follow logs) so contributors
can copy/paste a concrete command; reference the backend service name used in
the repo (backend) and show both the make-based and container-based options and
mention the follow/stream flag.
infrastructure/modules/service/tests/service.tftest.hcl (1)

122-160: Add a non-default health_check_path test case.

Current assertions validate the default path, but they don’t prove that overrides work (or that / isn’t hardcoded).

➕ Suggested test addition
+run "test_task_definition_health_check_path_override" {
+  command = plan
+
+  variables {
+    health_check_path = "/healthz"
+  }
+
+  assert {
+    condition     = strcontains(local.container_definition.healthCheck.command[1], ":${var.container_port}/healthz")
+    error_message = "Health check command must honor overridden health_check_path."
+  }
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@infrastructure/modules/service/tests/service.tftest.hcl` around lines 122 -
160, Add a new test run to assert non-default health_check_path is respected:
create another run block (similar to run
"test_task_definition_has_container_health_check") that sets
var.health_check_path to a custom value (e.g., "/custom") and then assert that
local.container_definition.healthCheck.command[1] contains
":${var.container_port}${var.health_check_path}" (matching the custom path) to
ensure the path is not hardcoded to "/". Keep the checks referencing
local.container_definition.healthCheck and
var.container_port/var.health_check_path so the test verifies overrides work.
frontend/__tests__/e2e/pages/BoardCandidates.spec.ts (1)

4-7: Decouple E2E test expectations from hard-coded board years

The test assumes 2025 exists and 2020 does not exist without documenting this assumption or validating it at runtime. Since board data is synced from the external www-board-candidates GitHub repository, the test becomes brittle if that repository changes. Consider sourcing valid and invalid years from the database state at test time, or documenting why these specific years are guaranteed to exist in the test environment.

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

In `@frontend/__tests__/e2e/pages/BoardCandidates.spec.ts` around lines 4 - 7,
Replace the brittle hard-coded years in the E2E test by dynamically deriving a
valid and invalid year at runtime: call the app's public API or test helper that
returns available board years (or query the test DB) and set validYear to one
returned value (e.g., first/most recent), then set invalidYear to a year
guaranteed absent (e.g., min(returnedYears) - 1) so validUrl and invalidUrl are
built from those variables; if no runtime source is available, add an explicit
comment in BoardCandidates.spec.ts explaining why '2025' and '2020' are
guaranteed in this test environment.
frontend/__tests__/unit/components/CardDetailsPage.test.tsx (1)

991-1014: Add click assertions for the new pagination controls.

These tests currently assert render state only; please also verify handler invocation to catch wiring regressions.

Suggested test strengthening
-    it('renders Show More button when onLoadMorePullRequests is provided', () => {
+    it('renders Show More button and triggers load callback', () => {
+      const onLoadMorePullRequests = jest.fn()
       render(
         <CardDetailsPage
           {...defaultProps}
           type="module"
           pullRequests={mockPullRequests as unknown as PullRequest[]}
-          onLoadMorePullRequests={jest.fn()}
+          onLoadMorePullRequests={onLoadMorePullRequests}
         />
       )
-      expect(screen.getByRole('button', { name: /Show more/i })).toBeInTheDocument()
+      const showMore = screen.getByRole('button', { name: /Show more/i })
+      expect(showMore).toBeInTheDocument()
+      fireEvent.click(showMore)
+      expect(onLoadMorePullRequests).toHaveBeenCalledTimes(1)
     })

-    it('renders Show Less button when onResetPullRequests is provided', () => {
+    it('renders Show Less button and triggers reset callback', () => {
+      const onResetPullRequests = jest.fn()
       render(
         <CardDetailsPage
           {...defaultProps}
           type="module"
           pullRequests={mockPullRequests as unknown as PullRequest[]}
-          onResetPullRequests={jest.fn()}
+          onResetPullRequests={onResetPullRequests}
           onLoadMorePullRequests={undefined}
         />
       )
-      expect(screen.getByRole('button', { name: /Show less/i })).toBeInTheDocument()
+      const showLess = screen.getByRole('button', { name: /Show less/i })
+      expect(showLess).toBeInTheDocument()
+      fireEvent.click(showLess)
+      expect(onResetPullRequests).toHaveBeenCalledTimes(1)
     })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/__tests__/unit/components/CardDetailsPage.test.tsx` around lines 991
- 1014, Update the two tests for CardDetailsPage to not only assert the buttons
render but also verify the handlers are invoked: replace the inline jest.fn()
with named mock functions (e.g., const onLoadMore = jest.fn() and const onReset
= jest.fn()), render CardDetailsPage with those mocks passed to
onLoadMorePullRequests and onResetPullRequests respectively, use userEvent.click
or fireEvent.click on the "Show more" / "Show less" buttons, and add expects
that onLoadMore and onReset were called (to ensure the component's pagination
wiring via onLoadMorePullRequests and onResetPullRequests works).
backend/tests/unit/apps/github/api/internal/nodes/issue_test.py (1)

91-120: Add a negative-offset boundary test for resolver parity.

The resolver clamps negative offsets to zero; adding that case here would lock in the pagination contract and prevent regressions.

Suggested additional assertion
     def test_pull_requests_resolver(self):
         """Test pull_requests field resolver with pagination."""
         mock_issue = Mock()
         mock_qs = Mock()

         mock_issue.pull_requests.all.return_value = mock_qs
         mock_qs.order_by.return_value = ["pr1", "pr2", "pr3", "pr4", "pr5"]

         field = self._get_field_by_name("pull_requests", IssueNode)
         result = field.base_resolver.wrapped_func(mock_issue)
         assert result == ["pr1", "pr2", "pr3", "pr4"]
         result = field.base_resolver.wrapped_func(mock_issue, limit=2, offset=2)
         assert result == ["pr3", "pr4"]
+        result = field.base_resolver.wrapped_func(mock_issue, limit=2, offset=-10)
+        assert result == ["pr1", "pr2"]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/tests/unit/apps/github/api/internal/nodes/issue_test.py` around lines
91 - 120, Add a negative-offset assertion to the pull_requests resolver test to
ensure offsets are clamped to zero: in test_pull_requests_resolver_security (or
next to test_pull_requests_resolver) call IssueNode's
field.base_resolver.wrapped_func(mock_issue, limit=10, offset=-5) after setting
up mock_issue.pull_requests.all().order_by().return_value and mock
normalize_limit, and assert the resolver returns the same result as offset=0 (or
that the underlying queryset was called with offset 0 behavior); reference
IssueNode, field.base_resolver.wrapped_func, and normalize_limit to find where
to add this check.
frontend/src/components/CardDetailsPage.tsx (1)

416-441: Inconsistent loading state handling between "Show more" and "Show less" buttons.

The "Show more" button (line 425) displays "Loading..." text when isFetchingMore is true, but the "Show less" button (line 437) always displays "Show less" even when disabled. While clicking "Show less" doesn't trigger a network request, having both buttons disabled with different text might be slightly confusing.

This is a minor UX consideration and the current implementation is functional.

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

In `@frontend/src/components/CardDetailsPage.tsx` around lines 416 - 441, Update
the "Show less" button in CardDetailsPage.tsx to mirror the loading/disabled UI
of the "Show more" button: when isFetchingMore is true, render a loading label
(e.g., "Loading...") and apply the same disabled state/classname logic for the
onResetPullRequests button as used for onLoadMorePullRequests so both buttons
behave and appear consistently while isFetchingMore is true.
frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx (1)

566-568: Unnecessary act() wrapper around fireEvent.click.

fireEvent from React Testing Library already wraps events in act() internally, so the explicit act() wrapper is redundant. This won't cause issues but adds unnecessary noise.

♻️ Proposed simplification
-    await act(async () => {
-      fireEvent.click(showMoreButton)
-    })
+    fireEvent.click(showMoreButton)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx` around lines
566 - 568, Remove the redundant act() wrapper around the fireEvent.click call in
the test: replace the block that calls await act(async () => {
fireEvent.click(showMoreButton) }) with a direct fireEvent.click(showMoreButton)
(or await if awaiting promises), updating the test that uses act and
showMoreButton so it relies on React Testing Library's internal act wrapping
rather than the explicit act call.
frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx (1)

403-467: Complex conditional logic for pagination controls.

The condition on lines 403-405 is quite complex:

{(hasMorePRs ||
  (issue.pullRequests || []).length > visibleCount ||
  (visibleCount > limit && (issue.pullRequests || []).length > limit)) && (

This combines three conditions that are partially redundant. The inner button visibility conditions (lines 407, 457) add additional checks. Consider simplifying:

♻️ Proposed simplification
-              {(hasMorePRs ||
-                (issue.pullRequests || []).length > visibleCount ||
-                (visibleCount > limit && (issue.pullRequests || []).length > limit)) && (
+              {/* Show pagination controls if there are more PRs to show or user can collapse */}
+              {(hasMorePRs || 
+                (issue.pullRequests?.length ?? 0) > visibleCount || 
+                visibleCount > limit) && (
                 <div className="mt-4 flex justify-start gap-4">
-                  {(hasMorePRs || (issue.pullRequests || []).length > visibleCount) && (
+                  {(hasMorePRs || (issue.pullRequests?.length ?? 0) > visibleCount) && (
                     <button ...>Show more</button>
                   )}
-                  {visibleCount > limit && (issue.pullRequests || []).length > limit && (
+                  {visibleCount > limit && (
                     <button ...>Show less</button>
                   )}
                 </div>
               )}

The (issue.pullRequests || []).length > limit check for "Show less" is redundant since visibleCount > limit implies the user has already expanded beyond the initial view.

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

In
`@frontend/src/app/my/mentorship/programs/`[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx
around lines 403 - 467, The pagination wrapper's conditional combines redundant
checks (hasMorePRs, (issue.pullRequests || []).length > visibleCount, and
(visibleCount > limit && (issue.pullRequests || []).length > limit)); simplify
by showing the container whenever either hasMorePRs is true or the total PR
count exceeds the initial limit or current visibleCount (i.e. use: hasMorePRs ||
(issue.pullRequests || []).length > limit || (issue.pullRequests || []).length >
visibleCount), and remove the redundant total-length check from the "Show less"
button (rely solely on visibleCount > limit to render the "Show less" button).
Update references in the JSX around hasMorePRs, visibleCount, limit, and
issue.pullRequests to use the simplified conditions and ensure the inner button
checks (the "Show more" click guard) still respect hasMorePRs and fetching
state.
frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx (2)

104-141: Consider extracting pagination logic to a custom hook for reusability.

The onLoadMorePullRequests handler contains complex pagination logic that's duplicated in the issue details page. Consider extracting this to a reusable hook like usePaginatedPullRequests.

This is a recommended improvement for maintainability given the duplication with ModuleIssueDetailsPage.

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

In
`@frontend/src/app/mentorship/programs/`[programKey]/modules/[moduleKey]/page.tsx
around lines 104 - 141, Extract the pagination logic inside the
onLoadMorePullRequests handler into a reusable custom hook (suggested name:
usePaginatedPullRequests) that encapsulates state (visibleCount, isFetchingMore,
hasMorePRs), the fetchMore invocation (including variables and updateQuery
merging logic that references programModule.recentPullRequests), and the
boundary checks/setters (setIsFetchingMore, setVisibleCount). Replace the inline
handler in page.tsx with the hook's onLoadMore function and return the necessary
values from the hook so ModuleIssueDetailsPage can reuse it as well; ensure the
hook API accepts parameters like programKey, moduleKey, limit, and the base
recentPullRequests to compute currentLength and determine hasMore behavior.

39-45: Pagination state initialization logic could be clearer.

The useEffect sets hasMorePRs based on whether the initial PR count equals or exceeds limit:

if (prCount <= limit) {
  setHasMorePRs(prCount >= limit)  // true if prCount === limit
}

This logic is correct (if exactly 4 PRs are returned, there might be more), but the nested condition is a bit confusing. Consider simplifying:

♻️ Proposed simplification
   useEffect(() => {
     const prCount = data?.getModule?.recentPullRequests?.length
     if (prCount == null) return
-    if (prCount <= limit) {
-      setHasMorePRs(prCount >= limit)
-    }
+    // If we received exactly `limit` PRs, there might be more on the server
+    // If we received fewer, we've exhausted the list
+    setHasMorePRs(prCount >= limit)
   }, [data, limit])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/mentorship/programs/`[programKey]/modules/[moduleKey]/page.tsx
around lines 39 - 45, The current useEffect uses a nested condition that is
confusing: it only calls setHasMorePRs when prCount <= limit then sets it to
prCount >= limit; instead simplify by, after the null check, unconditionally
setting hasMorePRs based on the retrieved count (e.g., compute prCount =
data?.getModule?.recentPullRequests?.length; if (prCount == null) return;
setHasMorePRs(prCount >= limit)); update the useEffect that uses useEffect,
prCount, setHasMorePRs, data?.getModule?.recentPullRequests?.length and limit
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/src/components/Header.tsx`:
- Around line 99-104: The active-state and aria-current checks use the raw
link.href while the anchor's href uses a fallback ('/'), causing mismatches for
links without href; resolve the href once (e.g., create a resolvedHref =
link.href || '/') and use resolvedHref both for href={...} and in the comparison
with pathname (and aria-current) so the active styling and aria attributes match
the rendered href; update references to link.href in this component (where cn is
called and aria-current set) to use the resolved variable.

In `@frontend/src/utils/getIcsFileUrl.ts`:
- Around line 10-19: The event dates are extracted in UTC inside parseDate and
getEndDate but the ics library treats DateArray as local time unless you set
input types; update the eventAttributes object (where you build the event
payload for the ics package) to include startInputType: 'utc' and endInputType:
'utc' so the parsed DateArray from parseDate and getEndDate is interpreted as
UTC by the ics library.

In `@infrastructure/modules/service/main.tf`:
- Around line 18-25: The ECS container healthCheck command uses wget but the
images don't install it; update the Dockerfiles to install wget so the health
check works: in the backend Dockerfile add wget to the existing apk --no-cache
add make postgresql16-client=16.13-r0 invocation (i.e., include wget in that
package list), and in the frontend Dockerfile add apk add --no-cache wget in the
runner stage where apk update && apk upgrade --no-cache is run; alternatively
replace the healthCheck command with a tool already present or a shell-native
probe if you prefer not to add wget.

---

Nitpick comments:
In `@backend/tests/unit/apps/github/api/internal/nodes/issue_test.py`:
- Around line 91-120: Add a negative-offset assertion to the pull_requests
resolver test to ensure offsets are clamped to zero: in
test_pull_requests_resolver_security (or next to test_pull_requests_resolver)
call IssueNode's field.base_resolver.wrapped_func(mock_issue, limit=10,
offset=-5) after setting up
mock_issue.pull_requests.all().order_by().return_value and mock normalize_limit,
and assert the resolver returns the same result as offset=0 (or that the
underlying queryset was called with offset 0 behavior); reference IssueNode,
field.base_resolver.wrapped_func, and normalize_limit to find where to add this
check.

In `@CONTRIBUTING.md`:
- Line 276: Update the line "To confirm Slack is reaching your machine, check
the backend container logs." to include exact commands for viewing those logs:
add the project's make target name (e.g., the make target used for backend logs)
and the container orchestration command (e.g., the docker-compose or docker
command and the flag to follow logs) so contributors can copy/paste a concrete
command; reference the backend service name used in the repo (backend) and show
both the make-based and container-based options and mention the follow/stream
flag.

In `@frontend/__tests__/e2e/pages/BoardCandidates.spec.ts`:
- Around line 4-7: Replace the brittle hard-coded years in the E2E test by
dynamically deriving a valid and invalid year at runtime: call the app's public
API or test helper that returns available board years (or query the test DB) and
set validYear to one returned value (e.g., first/most recent), then set
invalidYear to a year guaranteed absent (e.g., min(returnedYears) - 1) so
validUrl and invalidUrl are built from those variables; if no runtime source is
available, add an explicit comment in BoardCandidates.spec.ts explaining why
'2025' and '2020' are guaranteed in this test environment.

In `@frontend/__tests__/unit/components/CardDetailsPage.test.tsx`:
- Around line 991-1014: Update the two tests for CardDetailsPage to not only
assert the buttons render but also verify the handlers are invoked: replace the
inline jest.fn() with named mock functions (e.g., const onLoadMore = jest.fn()
and const onReset = jest.fn()), render CardDetailsPage with those mocks passed
to onLoadMorePullRequests and onResetPullRequests respectively, use
userEvent.click or fireEvent.click on the "Show more" / "Show less" buttons, and
add expects that onLoadMore and onReset were called (to ensure the component's
pagination wiring via onLoadMorePullRequests and onResetPullRequests works).

In `@frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx`:
- Around line 566-568: Remove the redundant act() wrapper around the
fireEvent.click call in the test: replace the block that calls await act(async
() => { fireEvent.click(showMoreButton) }) with a direct
fireEvent.click(showMoreButton) (or await if awaiting promises), updating the
test that uses act and showMoreButton so it relies on React Testing Library's
internal act wrapping rather than the explicit act call.

In
`@frontend/src/app/mentorship/programs/`[programKey]/modules/[moduleKey]/page.tsx:
- Around line 104-141: Extract the pagination logic inside the
onLoadMorePullRequests handler into a reusable custom hook (suggested name:
usePaginatedPullRequests) that encapsulates state (visibleCount, isFetchingMore,
hasMorePRs), the fetchMore invocation (including variables and updateQuery
merging logic that references programModule.recentPullRequests), and the
boundary checks/setters (setIsFetchingMore, setVisibleCount). Replace the inline
handler in page.tsx with the hook's onLoadMore function and return the necessary
values from the hook so ModuleIssueDetailsPage can reuse it as well; ensure the
hook API accepts parameters like programKey, moduleKey, limit, and the base
recentPullRequests to compute currentLength and determine hasMore behavior.
- Around line 39-45: The current useEffect uses a nested condition that is
confusing: it only calls setHasMorePRs when prCount <= limit then sets it to
prCount >= limit; instead simplify by, after the null check, unconditionally
setting hasMorePRs based on the retrieved count (e.g., compute prCount =
data?.getModule?.recentPullRequests?.length; if (prCount == null) return;
setHasMorePRs(prCount >= limit)); update the useEffect that uses useEffect,
prCount, setHasMorePRs, data?.getModule?.recentPullRequests?.length and limit
accordingly.

In
`@frontend/src/app/my/mentorship/programs/`[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx:
- Around line 403-467: The pagination wrapper's conditional combines redundant
checks (hasMorePRs, (issue.pullRequests || []).length > visibleCount, and
(visibleCount > limit && (issue.pullRequests || []).length > limit)); simplify
by showing the container whenever either hasMorePRs is true or the total PR
count exceeds the initial limit or current visibleCount (i.e. use: hasMorePRs ||
(issue.pullRequests || []).length > limit || (issue.pullRequests || []).length >
visibleCount), and remove the redundant total-length check from the "Show less"
button (rely solely on visibleCount > limit to render the "Show less" button).
Update references in the JSX around hasMorePRs, visibleCount, limit, and
issue.pullRequests to use the simplified conditions and ensure the inner button
checks (the "Show more" click guard) still respect hasMorePRs and fetching
state.

In `@frontend/src/components/CardDetailsPage.tsx`:
- Around line 416-441: Update the "Show less" button in CardDetailsPage.tsx to
mirror the loading/disabled UI of the "Show more" button: when isFetchingMore is
true, render a loading label (e.g., "Loading...") and apply the same disabled
state/classname logic for the onResetPullRequests button as used for
onLoadMorePullRequests so both buttons behave and appear consistently while
isFetchingMore is true.

In `@infrastructure/modules/service/tests/service.tftest.hcl`:
- Around line 122-160: Add a new test run to assert non-default
health_check_path is respected: create another run block (similar to run
"test_task_definition_has_container_health_check") that sets
var.health_check_path to a custom value (e.g., "/custom") and then assert that
local.container_definition.healthCheck.command[1] contains
":${var.container_port}${var.health_check_path}" (matching the custom path) to
ensure the path is not hardcoded to "/". Keep the checks referencing
local.container_definition.healthCheck and
var.container_port/var.health_check_path so the test verifies overrides work.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 61d7c094-d1b8-42dd-927e-787c2eb4af39

📥 Commits

Reviewing files that changed from the base of the PR and between b13837e and 2b5502e.

⛔ Files ignored due to path filters (5)
  • backend/poetry.lock is excluded by !**/*.lock
  • frontend/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • frontend/src/types/__generated__/graphql.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/issueQueries.generated.ts is excluded by !**/__generated__/**
  • frontend/src/types/__generated__/moduleQueries.generated.ts is excluded by !**/__generated__/**
📒 Files selected for processing (40)
  • .github/dependabot.yml
  • .pre-commit-config.yaml
  • CONTRIBUTING.md
  • backend/apps/github/api/internal/nodes/issue.py
  • backend/apps/mentorship/api/internal/nodes/module.py
  • backend/apps/slack/management/commands/slack_check_invite_link.py
  • backend/tests/unit/apps/github/api/internal/nodes/issue_test.py
  • backend/tests/unit/apps/slack/commands/slack_check_invite_link_test.py
  • docker-compose/e2e/compose.yaml
  • docker-compose/fuzz/compose.yaml
  • frontend/__tests__/e2e/pages/BoardCandidates.spec.ts
  • frontend/__tests__/unit/components/Card.test.tsx
  • frontend/__tests__/unit/components/CardDetailsPage.test.tsx
  • frontend/__tests__/unit/components/LoadingSpinner.test.tsx
  • frontend/__tests__/unit/hooks/useBreadcrumbs.test.tsx
  • frontend/__tests__/unit/pages/Header.test.tsx
  • frontend/__tests__/unit/pages/ModuleDetailsPage.test.tsx
  • frontend/__tests__/unit/pages/ModuleIssueDetailsPage.test.tsx
  • frontend/__tests__/unit/pages/ProgramDetailsMentorship.test.tsx
  • frontend/__tests__/unit/pages/PublicMentorshipModulePage.test.tsx
  • frontend/__tests__/unit/utils/getIcsFileUrl.test.ts
  • frontend/package.json
  • frontend/src/app/api/health/route.ts
  • frontend/src/app/mentorship/programs/[programKey]/modules/[moduleKey]/page.tsx
  • frontend/src/app/my/mentorship/programs/[programKey]/modules/[moduleKey]/issues/[issueId]/page.tsx
  • frontend/src/components/Card.tsx
  • frontend/src/components/CardDetailsPage.tsx
  • frontend/src/components/Header.tsx
  • frontend/src/components/LoadingSpinner.tsx
  • frontend/src/hooks/useBreadcrumbs.ts
  • frontend/src/server/queries/issueQueries.ts
  • frontend/src/server/queries/moduleQueries.ts
  • frontend/src/types/card.ts
  • frontend/src/types/mentorship.ts
  • frontend/src/utils/getIcsFileUrl.ts
  • infrastructure/live/main.tf
  • infrastructure/modules/alb/variables.tf
  • infrastructure/modules/service/main.tf
  • infrastructure/modules/service/tests/service.tftest.hcl
  • infrastructure/modules/service/variables.tf
💤 Files with no reviewable changes (2)
  • frontend/tests/unit/components/Card.test.tsx
  • frontend/src/components/Card.tsx

Comment on lines 99 to +104
href={link.href || '/'}
className={cn(
'navlink px-3 py-2 text-slate-700 hover:text-slate-800 dark:text-slate-300 dark:hover:text-slate-200',
'navlink px-3 py-2 text-slate-700 transition-colors duration-200 hover:text-white dark:text-slate-300 dark:hover:text-blue-400',
pathname === link.href && 'font-bold text-blue-800 dark:text-white'
)}
aria-current="page"
aria-current={pathname === link.href ? 'page' : undefined}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Align active-state logic with the resolved href.

Line 99 uses a fallback ('/'), but Lines 102/104 compare against raw link.href. For links without href, the rendered URL is '/' while active styling and aria-current never match.

Suggested fix
-                  <Link
+                  {(() => {
+                    const resolvedHref = link.href || '/'
+                    const isActive = pathname === resolvedHref
+                    return (
+                  <Link
                     key={link.text}
-                    href={link.href || '/'}
+                    href={resolvedHref}
                     className={cn(
                       'navlink px-3 py-2 text-slate-700 transition-colors duration-200 hover:text-white dark:text-slate-300 dark:hover:text-blue-400',
-                      pathname === link.href && 'font-bold text-blue-800 dark:text-white'
+                      isActive && 'font-bold text-blue-800 dark:text-white'
                     )}
-                    aria-current={pathname === link.href ? 'page' : undefined}
+                    aria-current={isActive ? 'page' : undefined}
                   >
                     {link.text}
                   </Link>
+                    )
+                  })()}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/Header.tsx` around lines 99 - 104, The active-state
and aria-current checks use the raw link.href while the anchor's href uses a
fallback ('/'), causing mismatches for links without href; resolve the href once
(e.g., create a resolvedHref = link.href || '/') and use resolvedHref both for
href={...} and in the comparison with pathname (and aria-current) so the active
styling and aria attributes match the rendered href; update references to
link.href in this component (where cn is called and aria-current set) to use the
resolved variable.

Comment on lines 10 to +19
const parseDate = (date: string): DateArray => {
// ISO date string
const d = new Date(date)
return [d.getFullYear(), d.getMonth() + 1, d.getDate()]
return [d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()]
}

const getEndDate = (start: DateArray, end: DateArray): DateArray => {
if (start.join('-') === end.join('-')) {
const [y, m, d] = end
const nextDay = new Date(y, m - 1, d + 1)
return [nextDay.getFullYear(), nextDay.getMonth() + 1, nextDay.getDate()]
const nextDay = new Date(Date.UTC(y, m - 1, d + 1))
return [nextDay.getUTCFullYear(), nextDay.getUTCMonth() + 1, nextDay.getUTCDate()]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

For the npm icspackage (v3.x), when passingDateArrayvalues generated from UTC timestamps, do we needstartInputType: 'utc'andendInputType: 'utc' to prevent local-time date shifts?

💡 Result:

Yes, for the npm ics package (v3.x), when passing DateArray values generated from UTC timestamps, you need to set startInputType: 'utc' and endInputType: 'utc' to prevent local-time date shifts. The official documentation explicitly states that the default for startInputType is 'local', meaning DateArray values are interpreted as local time components unless specified otherwise. For endInputType, the default is the value of startInputType (local if unspecified). DateArray is [year, month, date, hour, minute] (1-indexed month). If generated from a UTC timestamp using local Date methods (e.g., new Date(utcTimestamp).getFullYear etc.), the array holds local time components for that UTC instant. Without 'utc', the library treats these as local time, causing it to compute the wrong UTC instant for ICS output (typically UTC by default via startOutputType/endOutputType), resulting in date/time shifts. To correctly interpret the array as UTC components (preserving the original UTC instant), set the input types to 'utc'. Examples from docs use local arrays like [2018, 5, 30, 6, 30] without inputType (implicit local). TypeScript definitions confirm optional input types as 'local' | 'utc'. The convertTimestampToArray(timestamp, inputType) utility (for number timestamps) takes an inputType parameter, supporting both timestamp and array inputs with analogous handling.

Citations:


Add startInputType and endInputType properties set to 'utc' in the eventAttributes object.

The UTC date extraction using getUTCFullYear(), getUTCMonth(), and getUTCDate() is correct, but the ics library interprets DateArray as local time by default. Without explicitly setting the input types to 'utc', dates will shift for users in non-UTC timezones.

Proposed fix
 const eventAttributes: EventAttributes = {
   start: startArray,
   end: finalEndArray,
+  startInputType: 'utc',
+  endInputType: 'utc',
   title: event.title,
   status: 'CONFIRMED',
   busyStatus: 'BUSY',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/utils/getIcsFileUrl.ts` around lines 10 - 19, The event dates
are extracted in UTC inside parseDate and getEndDate but the ics library treats
DateArray as local time unless you set input types; update the eventAttributes
object (where you build the event payload for the ics package) to include
startInputType: 'utc' and endInputType: 'utc' so the parsed DateArray from
parseDate and getEndDate is interpreted as UTC by the ics library.

Comment on lines +18 to +25
healthCheck = {
command = ["CMD-SHELL", "wget --spider http://$(hostname -i):${var.container_port}${var.health_check_path} > /dev/null 2>&1"]
interval = 30
retries = 3
startPeriod = 100
timeout = 5
}
image = "${aws_ecr_repository.main.repository_url}:${var.image_tag}"

Copy link
Copy Markdown
Contributor

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

🏁 Script executed:

#!/bin/bash
# Verify wget availability in Dockerfiles

echo "=== Checking backend Dockerfile for wget or BusyBox ==="
cat docker/backend/Dockerfile | grep -iE '(wget|busybox|alpine|apk add|apt-get install)' || echo "No explicit wget installation found"

echo ""
echo "=== Checking frontend Dockerfile for wget or BusyBox ==="
cat docker/frontend/Dockerfile | grep -iE '(wget|busybox|alpine|apk add|apt-get install)' || echo "No explicit wget installation found"

echo ""
echo "=== Checking base images ==="
grep -E '^FROM' docker/backend/Dockerfile docker/frontend/Dockerfile 2>/dev/null || echo "Could not find FROM statements"

Repository: OWASP/Nest

Length of output: 726


🌐 Web query:

Alpine Linux 3.23 wget availability default packages

💡 Result:

No, wget is not available in Alpine Linux 3.23 default packages. It is provided as a separate package in the main repository (version 1.25.0-r2), but not included in the minimal base installation (alpine-base meta-package), which consists of core components like alpine-baselayout, apk-tools, busybox, musl-utils, and openrc. Users must explicitly install it with apk add wget.

Citations:


🏁 Script executed:

#!/bin/bash
# Check if there are any health check related test files or documentation
find . -type f \( -name "*.tf" -o -name "*.py" -o -name "*.js" -o -name "*.sh" \) | xargs grep -l "healthCheck\|health_check" 2>/dev/null | head -20

Repository: OWASP/Nest

Length of output: 249


🏁 Script executed:

#!/bin/bash
echo "=== Backend Dockerfile (full content) ==="
cat docker/backend/Dockerfile

echo ""
echo "=== Frontend Dockerfile (full content) ==="
cat docker/frontend/Dockerfile

Repository: OWASP/Nest

Length of output: 7287


Health check implementation will fail—wget is not installed in the container images.

The health check command relies on wget, but neither the backend nor frontend Dockerfile explicitly installs it. Alpine Linux 3.23 does not include wget by default; it must be explicitly installed with apk add wget.

Both containers will fail health checks when ECS attempts to execute the command.

Required fix: Add apk add --no-cache wget to the apk add statements in both Dockerfiles:

  • Backend: Add to the apk --no-cache add make postgresql16-client=16.13-r0 line
  • Frontend (runner stage): Add to the apk update && apk upgrade --no-cache section

Alternatively, use a built-in health check mechanism (e.g., a health check script that doesn't depend on external tools, or use curl if it's available in the base images).

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

In `@infrastructure/modules/service/main.tf` around lines 18 - 25, The ECS
container healthCheck command uses wget but the images don't install it; update
the Dockerfiles to install wget so the health check works: in the backend
Dockerfile add wget to the existing apk --no-cache add make
postgresql16-client=16.13-r0 invocation (i.e., include wget in that package
list), and in the frontend Dockerfile add apk add --no-cache wget in the runner
stage where apk update && apk upgrade --no-cache is run; alternatively replace
the healthCheck command with a tool already present or a shell-native probe if
you prefer not to add wget.

@arkid15r
arkid15r enabled auto-merge April 13, 2026 23:58
@arkid15r
arkid15r disabled auto-merge April 13, 2026 23:58
@arkid15r
arkid15r merged commit ed241e6 into OWASP:feature/nestbot-ai-assistant Apr 13, 2026
63 of 66 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 9, 2026
4 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.