Skip to content

feat(gitprovider): GitLab sync — Issue entity#777

Merged
FelixTJDietrich merged 28 commits intomainfrom
feat/gitprovider-gitlab-sync-issue
Mar 2, 2026
Merged

feat(gitprovider): GitLab sync — Issue entity#777
FelixTJDietrich merged 28 commits intomainfrom
feat/gitprovider-gitlab-sync-issue

Conversation

@FelixTJDietrich
Copy link
Copy Markdown
Collaborator

@FelixTJDietrich FelixTJDietrich commented Feb 28, 2026

Description

Implements GitLab Issue sync to the Issue entity, completing the data model for GitLab repository activity tracking.

Core implementation:

  • GitLabIssueProcessor — state mapping (openedOPEN, closedCLOSED), confidential filtering, author/label/assignee resolution within @Transactional boundary
  • GitLabIssueSyncService — paginated GraphQL sync with incremental updatedAfter support
  • GitLabIssueMessageHandler — handles issue and confidential_issue webhook events
  • BaseGitLabProcessor — shared label/user resolution with deterministic label IDs (avoids group-level label PK collisions)

Infrastructure changes:

  • Provider discriminator column on all BaseGitServiceEntity tables with Liquibase migration
  • Provider-scoped unique constraints (user login, organization, repository)
  • Organization.githubIdOrganization.providerId rename
  • Issue sync wired into WorkspaceActivationService after project sync
  • ERD documentation updated

Verified with live sync against 59 GitLab projects: 1122/1122 issues synced correctly with 758 label associations and 270 assignees.

Closes #715

How to Test

  • mvn verify -P'!quick' — runs 1026 unit + 419 integration tests
  • Live sync: set GITLAB_ENABLED=true with PAT and group path, enable SYNC_RUN_ON_STARTUP=true
  • Verify: SELECT COUNT(*) FROM issue WHERE provider='GITLAB' should match GitLab REST API count
  • Confidential filtering: SELECT * FROM issue WHERE provider='GITLAB' should contain no confidential issues

Summary by CodeRabbit

  • New Features

    • Added GitLab support (webhooks, sync, GraphQL) and GitLab issue sync; multi-provider support with selectable Git providers and improved GraphQL editor features.
  • Chores / Migrations

    • Database migration and schema changes to enable provider-scoped identities and backfill existing data; build tooling updated to generate separate GitHub/GitLab GraphQL clients.
  • Tests

    • Expanded unit and integration tests covering GitLab flows, provider-scoped behavior, and migration scenarios.

Implement full GitLab Issue sync with state mapping, confidential
filtering, label/assignee resolution, and webhook event handling.

Closes #715

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 28, 2026 11:31
@FelixTJDietrich FelixTJDietrich requested a review from a team as a code owner February 28, 2026 11:31
@github-actions github-actions Bot added documentation Improvements or additions to documentation application-server Spring Boot server: APIs, business logic, database feature New feature or enhancement dependencies Package updates, version bumps, lock file changes size:XXL This PR changes 1000+ lines, ignoring generated files. labels Feb 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds multi-provider support (GitHub + GitLab): new GitProvider entity/repo, provider-scoped native IDs and unique constraints with Liquibase migration, widespread repo/service/entity refactors to thread providerId, new GitLab webhook DTOs/handlers/processors and GraphQL-based GitLab issue sync, build/codegen updates, and extensive test adaptations.

Changes

Cohort / File(s) Summary
GraphQL config
\.graphqlrc.yml
Split GraphQL projects into github and gitlab; updated documents patterns, include scopes and enabled languageService features.
Build / Codegen
server/application-server/pom.xml
Added dedicated GitLab GraphQL codegen execution; separate generated sources (graphql-github, graphql-gitlab); adjusted add-source, PMD excludes and post-processing targets.
DB changelogs & schema
server/application-server/src/main/resources/db/changelog/1772284124265_changelog.xml, server/intelligence-service/src/shared/db/schema.ts, server/application-server/src/main/resources/db/master.xml, docs/contributor/erd/schema.mmd
Introduced git_provider table; added native_id and provider_id to many tables; converted id columns to identity; added provider-scoped unique constraints; master include added.
Provider core
server/application-server/src/main/java/.../gitprovider/common/GitProvider.java, .../GitProviderRepository.java, .../GitProviderType.java
New JPA entity GitProvider, repository GitProviderRepository, and enum GitProviderType (GITHUB, GITLAB).
Base entities & context
server/application-server/src/main/java/.../gitprovider/common/BaseGitServiceEntity.java, .../ProcessingContext.java
Base entity now stores nativeId and provider (provider FK); id generation adjusted to IDENTITY; ProcessingContext carries GitProvider and providerId helpers.
Domain providerization (many files)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/**
Widespread changes: repositories and upsert SQL switch from id/githubId to nativeId+providerId; many repository interfaces gained provider-scoped finders (e.g., findByNativeIdAndProviderId) and upsert signatures; services and processors thread provider/GitProvider resolved by server URL.
GitLab feature: DTOs, processors, handlers, sync
server/application-server/src/main/java/.../gitlab/**, server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql
New GitLab webhook DTO records, BaseGitLabProcessor, GitLabIssueMessageHandler, GitLabIssueProcessor, GitLabIssueSyncService with cursored GraphQL query, timestamp parsing, label/user resolution and sync logic.
GitHub adaptations
server/application-server/src/main/java/.../github/**, .../CommitAuthorEnrichmentService.java, sync services
Propagated GitProvider resolution for GitHub flows; threaded providerId into user upserts, relationship updates, sync services and commit enrichment; many handlers updated to resolve provider and pass providerId.
Repositories: new queries & upserts
server/application-server/src/main/java/.../*Repository.java
Added provider-scoped repository methods: findByNativeIdAndProviderId, new upsert signatures accepting nativeId+providerId, upsertFromSnapshot, updateLastSyncAt and other provider-aware queries.
Liquibase & DB migration file
server/application-server/src/main/resources/db/changelog/*.xml
Changelog implements provider table, backfills native_id/provider_id, converts id to identity, and updates unique constraints across multiple tables.
Tests & test infra
server/application-server/src/test/java/...
Extensive test updates: new GitLab unit/integration tests; hundreds of GitHub tests adapted to nativeId/provider wiring; TestUserFactory/TestUserConfig and many integration tests now seed/expect GitProvider and nativeId.
Build/config & misc
.graphqlrc.yml, server/application-server/pom.xml, code quality tests
Config and test-exemption updates for new native SQL signatures and added generated sources; code quality test updates to allow new native SQL methods.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(100,149,237,0.5)
    participant Webhook as GitLab Webhook
    participant Handler as GitLabIssueMessageHandler
    participant Processor as GitLabIssueProcessor
    participant UserRepo as UserRepository
    participant IssueRepo as IssueRepository
    participant Events as ApplicationEventPublisher
    end
    Webhook->>Handler: POST issue event
    Handler->>Handler: validate payload, skip if confidential
    Handler->>Handler: resolve ProcessingContext (Repository + GitProvider)
    Handler->>Processor: process(event, context)
    Processor->>UserRepo: findOrCreateUser(author/globalId, providerId)
    UserRepo-->>Processor: User
    Processor->>IssueRepo: upsertCore(nativeId, providerId, ...)
    IssueRepo-->>Processor: Issue
    alt new issue
        Processor->>Events: publish IssueCreated
    else closed/reopened
        Processor->>Events: publish IssueClosed/IssueReopened
    end
    Processor-->>Handler: return Issue
Loading
sequenceDiagram
    rect rgba(60,179,113,0.5)
    participant Sync as GitLabIssueSyncService
    participant GraphQL as GitLabGraphQlClient
    participant Processor as GitLabIssueProcessor
    participant UserRepo as UserRepository
    participant IssueRepo as IssueRepository
    end
    Sync->>GraphQL: Execute GetProjectIssues(projectPath, updatedAfter, after)
    GraphQL-->>Sync: nodes + pageInfo
    loop per issue node
        Sync->>Processor: processFromSync(syncIssueData, repository)
        Processor->>UserRepo: findOrCreateUser(authorGlobalId, providerId)
        UserRepo-->>Processor: User
        Processor->>IssueRepo: upsertCore(nativeId, providerId, ...)
        IssueRepo-->>Processor: Issue
    end
    Sync-->>Sync: advance cursor / handle rate limits
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels

maintenance

Poem

🐇 I hopped through schemas, native ids in tow,

Two providers now share the same meadow,
Webhooks and syncs queue neatly in line,
Labels and users found home just fine,
A little rabbit cheers: multi-provider—hooray!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The changeset contains substantial out-of-scope modifications: multi-provider architecture refactoring (GitProvider entity, provider-scoped unique constraints, synthetic PKs), workspace and sync service updates, GraphQL configuration changes, and extensive test infrastructure updates that extend beyond GitLab issue sync. Separate infrastructure/provider refactoring into a dedicated PR; this PR should focus on GitLab issue sync logic only. Consider splitting into: (1) provider architecture foundation and (2) GitLab issue sync implementation.
Docstring Coverage ⚠️ Warning Docstring coverage is 46.51% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(gitprovider): GitLab sync — Issue entity' directly describes the main change: adding GitLab issue synchronization functionality to the Hephaestus gitprovider module.
Linked Issues check ✅ Passed The pull request fully implements all in-scope requirements from issue #715: GitLabIssueProcessor for state mapping and confidential filtering, GitLabIssueSyncService for pagination and incremental sync, message handler for webhook events, and comprehensive test coverage.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/gitprovider-gitlab-sync-issue

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.

@dosubot dosubot Bot added the webhook-ingest TypeScript webhook receiver: GitHub/GitLab event intake label Feb 28, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 28, 2026

📚 Documentation Preview

Preview has been removed (PR closed)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds GitLab Issue ingestion (webhook + GraphQL sync) and introduces multi-provider persistence support so GitHub and GitLab entities can coexist in the same schema.

Changes:

  • Implement GitLab Issue processing, webhook handling, and paginated GraphQL issue sync (with confidential issue skipping).
  • Add provider discriminator across BaseGitServiceEntity tables + provider-scoped uniqueness, and rename Organization.githubIdproviderId.
  • Update GraphQL tooling/codegen to support separate GitHub/GitLab schemas and generated sources; update tests and ERD.

Reviewed changes

Copilot reviewed 65 out of 65 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
server/application-server/src/test/java/de/tum/in/www1/hephaestus/practices/PracticesControllerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubMembershipMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/subissue/github/GitHubSubIssuesMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessorTest.java Align GitLab repo IDs to negated entity IDs in tests.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectSyncServiceTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessorTest.java Update GitLab org ID expectations (negated IDs + provider param).
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessorTest.java New unit tests for GitLab Issue processing/state/confidential handling.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java New unit tests for GitLab Issue message routing/validation/confidential skipping.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java New integration tests for GitLab Issue webhook → DB persistence.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationTargetMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationRepositoriesMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionProcessorIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionMessageHandlerIntegrationTest.java Update org test setup to use providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessorTest.java New unit tests for GitLab base helpers (timestamps/users/labels/context/ID mapping).
server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql Add GitLab GraphQL query to page through project issues.
server/application-server/src/main/resources/db/master.xml Include new Liquibase changelog for multi-provider support.
server/application-server/src/main/resources/db/changelog/1772200000000_changelog.xml Add provider columns, rename org provider ID column, and provider-scoped uniqueness.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java Provider-aware user upsert/locking for PAT bootstrap (GitHub + GitLab paths).
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceInstallationService.java Provider-aware user upsert/locking for ownership bootstrap.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceActivationService.java Trigger GitLab issue sync after GitLab project sync during activation.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/github/GitHubUserProcessor.java Pass provider into login-lock/rename/upsert operations.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserRepository.java Add provider-scoped advisory lock + conflict resolution + provider in upsert SQL.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessor.java Store GitLab repos with negated IDs and provider=GITLAB.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java Include provider when upserting organizations.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/Repository.java Update uniqueness to (provider, name_with_owner).
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessor.java Store GitLab orgs with negated IDs and provider=GITLAB via upsert.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java Update logging to use providerId.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java Rename githubId usage to providerId in org processing.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java Update org identity upsert to use providerId lookups.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationRepository.java Rename find method + add provider parameter to upsert.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/Organization.java Rename column + update unique constraints to be provider-scoped.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/dto/GitLabIssueEventDTO.java New DTO for GitLab issue webhooks with action parsing/confidential flag.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java New GitLab GraphQL issue sync service (pagination + updatedAfter).
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java New GitLab Issue processor for webhook + sync, state mapping, relationships, events.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandler.java New GitLab webhook handler routing issue actions to processor methods.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookUser.java New shared GitLab webhook user DTO.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookProject.java New shared GitLab webhook project DTO.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookLabel.java New shared GitLab webhook label DTO.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/GitLabSyncConstants.java Add helper to negate raw IDs and extract negated IDs from global IDs.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java New shared base for GitLab processors (user/label resolution, timestamps, context).
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java Add provider enum column to all base git service entities.
server/application-server/pom.xml Split GitHub/GitLab GraphQL codegen output directories + include both in build.
docs/contributor/erd/schema.mmd Update ERD to reflect new provider columns and org provider ID rename.
.graphqlrc.yml Configure VS Code GraphQL tooling for separate GitHub and GitLab projects.
Comments suppressed due to low confidence (2)

docs/contributor/erd/schema.mmd:321

  • ERD lists the Organization.provider column twice (duplicate lines). Please remove the duplicate entry to keep the schema documentation accurate.
        BIGINT provider_id UK "NOT NULL"
        VARCHAR(255) html_url
        VARCHAR(255) login UK "NOT NULL"
        VARCHAR(255) name
        TIMESTAMPTZ last_sync_at
        VARCHAR(10) provider UK "NOT NULL"
        VARCHAR(10) provider UK "NOT NULL"

docs/contributor/erd/schema.mmd:266

  • ERD marks provider columns on junction tables like IssueAssignee / IssueLabel / IssueComment, but the Liquibase migration in this PR only adds provider to BaseGitServiceEntity tables (and not to these junction tables). Either add the columns in the migration or adjust the ERD so it matches the actual schema.
    IssueAssignee {
        BIGINT issue_id PK,FK
        BIGINT user_id PK,FK
    }

    IssueBlocking {
        BIGINT blocked_issue_id PK,FK
        BIGINT blocking_issue_id PK,FK
    }

    IssueComment {
        BIGINT id PK
        TIMESTAMPTZ created_at
        TIMESTAMPTZ updated_at
        VARCHAR(255) author_association
        VARCHAR(255) html_url
        BIGINT author_id FK
        BIGINT issue_id FK
        TEXT body
        VARCHAR(10) provider "NOT NULL"
    }

    IssueLabel {
        BIGINT issue_id PK,FK

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java (1)

144-166: ⚠️ Potential issue | 🟠 Major

Apply Lombok annotations to align with coding guidelines.

The delete method logic is correct and properly handles cascade deletion. However, the class violates two coding guidelines:

  1. Constructor injection: Use @RequiredArgsConstructor instead of manual constructor
  2. Logging: Use @Slf4j annotation instead of LoggerFactory.getLogger()

These should be refactored for consistency with the coding guidelines for server/application-server/**/*.java files.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java`
around lines 144 - 166, The class GitHubOrganizationProcessor currently uses an
explicit constructor and a manually-created logger; replace these with Lombok
annotations by removing the explicit constructor and LoggerFactory usage and
adding `@RequiredArgsConstructor` and `@Slf4j` to the class declaration so Spring
will still inject organizationRepository and projectIntegrityService via
constructor injection and logging calls (e.g., log.info(...)) use the
Lombok-provided logger; also add the corresponding lombok imports and remove
now-unused manual logger/imports.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java (1)

259-272: ⚠️ Potential issue | 🔴 Critical

Critical bug: Wrong provider used for GitLab user upsert.

The syncGitLabUserForPAT method passes "GITHUB" as the provider parameter when acquiring locks and upserting users, but this method handles GitLab PAT workspace bootstrap. This will:

  1. Acquire advisory locks in the wrong provider namespace
  2. Store GitLab users with provider='GITHUB' instead of 'GITLAB'
  3. Cause potential conflicts with provider-scoped unique constraints
🐛 Proposed fix
-                userRepository.acquireLoginLock(login, "GITHUB");
-                userRepository.freeLoginConflicts(login, userInfo.id(), "GITHUB");
+                userRepository.acquireLoginLock(login, "GITLAB");
+                userRepository.freeLoginConflicts(login, userInfo.id(), "GITLAB");
                 userRepository.upsertUser(
                     userInfo.id(),
                     login,
                     name,
                     avatar,
                     webUrl,
                     User.Type.USER.name(),
                     null,
                     null,
-                    null,
-                    "GITHUB"
+                    null,
+                    "GITLAB"
                 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java`
around lines 259 - 272, In syncGitLabUserForPAT the provider constant is
incorrectly set to "GITHUB": change all provider usages in that
method—userRepository.acquireLoginLock(login, "GITHUB"),
userRepository.freeLoginConflicts(login, userInfo.id(), "GITHUB"), and
userRepository.upsertUser(..., "GITHUB")—to the GitLab provider value (e.g.
"GITLAB" or the corresponding enum/constant used elsewhere) so locks and the
upsert store the correct provider namespace and avoid cross-provider conflicts.
🧹 Nitpick comments (8)
server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql (1)

26-41: Nested collection limits may truncate data in edge cases.

The query uses labels(first: 100) and assignees(first: 20) without pagination. Issues exceeding these limits will have truncated label/assignee data. This is typically acceptable for most projects, but consider documenting this limitation or adding warning logs if the counts approach these limits during sync.

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

In
`@server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql`
around lines 26 - 41, The GraphQL query in GetProjectIssues.graphql uses fixed
limits labels(first: 100) and assignees(first: 20) which can truncate data for
issues with more labels/assignees; update the sync logic that executes this
query (or the code that processes its results) to detect when the returned
label/assignee counts equal the limit and emit a warning log or metric
mentioning labels(first:100) or assignees(first:20), and also add a brief
comment in GetProjectIssues.graphql documenting the imposed limits and expected
behavior so maintainers know this is intentional.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java (2)

306-306: Consider using enum constant for type safety.

The hardcoded string "GITHUB" is appropriate for this GitHub-specific service, but using GitProviderType.GITHUB.name() would provide compile-time safety against typos and alignment with the enum used elsewhere in the codebase.

♻️ Suggested refactor
+import de.tum.in.www1.hephaestus.workspace.GitProviderType;
 ...
             // Use upsert for thread-safe concurrent inserts
-            organizationRepository.upsert(databaseId, databaseId, login, name, avatarUrl, url, "GITHUB");
+            organizationRepository.upsert(databaseId, databaseId, login, name, avatarUrl, url, GitProviderType.GITHUB.name());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java`
at line 306, The call to organizationRepository.upsert currently passes the
hardcoded string "GITHUB"; replace this literal with the enum constant to ensure
type safety by using GitProviderType.GITHUB.name() (or
GitProviderType.GITHUB.toString() if codebase prefers) when invoking
organizationRepository.upsert so the provider type comes from the
GitProviderType enum rather than a raw string.

322-322: Same refactor applies here.

Apply the same enum constant change for consistency with line 306.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java`
at line 322, The call to organizationRepository.upsert(...) is using the string
literal "GITHUB"; change it to use the same enum constant used earlier (the one
used at line 306) for provider identification so both calls are
consistent—replace the final argument "GITHUB" in
organizationRepository.upsert(databaseId, databaseId, login, name, avatarUrl,
url, "GITHUB") with the enum constant (the same symbol used in the nearby
refactor).
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java (1)

119-127: Minor: Unused variable.

The repo variable is assigned but not used. Since setupRepository() is called only for its side effects (mocking), the variable capture can be removed.

♻️ Suggested cleanup
         `@Test`
         `@DisplayName`("update action routes to process()")
         void updateAction_routesToProcess() throws IOException {
             GitLabIssueEventDTO event = createEvent("update", "closed", false);
-            Repository repo = setupRepository();
+            setupRepository();

             Message msg = mockMessage(event);
             handler.onMessage(msg);

Apply similarly to closeAction_routesToProcessClosed, reopenAction_routesToProcessReopened, and unknownAction_skipsProcessing.

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java`
around lines 119 - 127, Remove the unused local variable `repo` in the test
method `updateAction_routesToProcess()` by calling `setupRepository()` for its
side effects without assigning its return value (i.e., replace `Repository repo
= setupRepository();` with simply `setupRepository();`); apply the same cleanup
to the other tests mentioned (`closeAction_routesToProcessClosed`,
`reopenAction_routesToProcessReopened`, and `unknownAction_skipsProcessing`)
where `setupRepository()` is invoked but its return value is unused.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java (1)

17-30: Consider renaming the parameter for consistency.

The method parameter is named githubId but it's now being assigned to a provider-agnostic providerId field. For clarity and consistency with the multi-provider architecture, consider renaming:

  • Parameter githubIdproviderId (or externalId)
  • Update the Javadoc to reflect provider-agnostic semantics
♻️ Suggested refactor
     /**
-     * Ensure an organization row exists (by stable GitHub org id) and keep its login up to date (rename-safe).
+     * Ensure an organization row exists (by stable provider org id) and keep its login up to date (rename-safe).
      */
     `@Transactional`
-    public Organization upsertIdentity(long githubId, String login) {
+    public Organization upsertIdentity(long providerId, String login) {
         if (login == null || login.isBlank()) {
             throw new IllegalArgumentException("login required");
         }

         Organization organization = organizations
-            .findByProviderId(githubId)
+            .findByProviderId(providerId)
             .orElseGet(() -> {
                 Organization o = new Organization();
-                o.setId(githubId);
-                o.setProviderId(githubId);
+                o.setId(providerId);
+                o.setProviderId(providerId);
                 return o;
             });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java`
around lines 17 - 30, Rename the method parameter githubId to a
provider-agnostic name (e.g., providerId or externalId) in the upsertIdentity
method and update its Javadoc to reflect provider-agnostic semantics; update all
references inside the method (calls to organizations.findByProviderId, o.setId,
o.setProviderId and any other usages) and adjust any callers of upsertIdentity
to use the new parameter name so signatures remain consistent across the
multi-provider architecture.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java (2)

405-411: Unused helper method labelNames().

The labelNames() method is defined but never used in this test file. Consider removing it to reduce dead code.

🧹 Remove unused method
-    private Set<String> labelNames(Issue issue) {
-        return issue
-            .getLabels()
-            .stream()
-            .map(l -> l.getName())
-            .collect(Collectors.toSet());
-    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java`
around lines 405 - 411, Remove the dead helper method labelNames() from
GitLabIssueMessageHandlerIntegrationTest (it is defined but never referenced);
locate the private Set<String> labelNames(Issue issue) method and delete it to
eliminate unused code and clean up the test class.

415-454: Consider thread-safety for event listener in parallel test execution.

The GitLabTestEventListener uses ArrayList which is not thread-safe. While individual tests clear the listener in @BeforeEach, if Spring re-uses the same listener instance across parallel test executions, concurrent event publishing could cause issues. Since the coding guidelines indicate tests may run in parallel, consider using thread-safe collections.

🛡️ Use thread-safe collections
 `@Component`
 static class GitLabTestEventListener {

-    private final List<DomainEvent.IssueCreated> createdEvents = new ArrayList<>();
-    private final List<DomainEvent.IssueClosed> closedEvents = new ArrayList<>();
-    private final List<DomainEvent.IssueReopened> reopenedEvents = new ArrayList<>();
+    private final List<DomainEvent.IssueCreated> createdEvents = Collections.synchronizedList(new ArrayList<>());
+    private final List<DomainEvent.IssueClosed> closedEvents = Collections.synchronizedList(new ArrayList<>());
+    private final List<DomainEvent.IssueReopened> reopenedEvents = Collections.synchronizedList(new ArrayList<>());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java`
around lines 415 - 454, The GitLabTestEventListener uses non-thread-safe
ArrayList fields (createdEvents, closedEvents, reopenedEvents) which can fail
under parallel event publishing; replace those ArrayList instances with
thread-safe collections (e.g., CopyOnWriteArrayList or ConcurrentLinkedQueue
wrapped as lists) and keep the existing
getCreatedEvents/getClosedEvents/getReopenedEvents to return snapshots (new
ArrayList<>(...)) and ensure clear() safely empties the thread-safe collections;
update the field initializations in class GitLabTestEventListener and any direct
operations (add/clear) to use the chosen concurrent collection API.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java (1)

32-50: Consider using Lombok annotations for consistency.

The class uses manual constructor and Logger declaration. Per coding guidelines, consider using @RequiredArgsConstructor for constructor injection and @Slf4j for logging. However, the current manual approach is functionally correct.

♻️ Optional Lombok refactor
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;

+@Slf4j
 `@Service`
 `@ConditionalOnProperty`(prefix = "hephaestus.gitlab", name = "enabled", havingValue = "true")
+@RequiredArgsConstructor
 public class GitLabIssueSyncService {

-    private static final Logger log = LoggerFactory.getLogger(GitLabIssueSyncService.class);
-
     private static final String GET_PROJECT_ISSUES_DOCUMENT = "GetProjectIssues";

     private final GitLabGraphQlClientProvider graphQlClientProvider;
     private final GitLabIssueProcessor issueProcessor;
     private final GitLabProperties gitLabProperties;
-
-    public GitLabIssueSyncService(
-        GitLabGraphQlClientProvider graphQlClientProvider,
-        GitLabIssueProcessor issueProcessor,
-        GitLabProperties gitLabProperties
-    ) {
-        this.graphQlClientProvider = graphQlClientProvider;
-        this.issueProcessor = issueProcessor;
-        this.gitLabProperties = gitLabProperties;
-    }

As per coding guidelines: "Use constructor injection via @RequiredArgsConstructor annotation" and "Use Lombok annotations: @RequiredArgsConstructor, @Slf4j".

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java`
around lines 32 - 50, Replace the manual Logger and constructor in
GitLabIssueSyncService with Lombok annotations: add `@Slf4j` and
`@RequiredArgsConstructor` on the GitLabIssueSyncService class, remove the
explicit Logger field "log" and the explicit constructor, and keep existing
final fields (graphQlClientProvider, issueProcessor, gitLabProperties) and the
static constant GET_PROJECT_ISSUES_DOCUMENT unchanged so Lombok generates the
constructor and logging instance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributor/erd/schema.mmd`:
- Around line 320-321: The ERD generator output contains a duplicated column
definition for Organization.provider (two identical lines "VARCHAR(10) provider
UK 'NOT NULL'"); update the generator source so it emits the provider column
only once (remove the duplicate emission in the code that builds Organization
schema), then regenerate the diagram using the documented command `npm run
db:generate-erd-docs` to update docs/contributor/erd/schema.mmd; locate the
generator logic that constructs the Organization columns (search for
Organization.provider or the function that builds Organization schema) and
ensure it deduplicates or prevents emitting the provider field twice before
regenerating the ERD.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`:
- Around line 331-348: The upsertIssue method sets
issue.setProvider(GitProviderType.GITLAB) but may not persist that change
because callers (e.g., process()) only save when labels/assignees changed;
modify upsertIssue (or processFromSync) to persist the provider change by saving
the Issue after setting provider (or return a boolean/flag from upsertIssue
indicating provider was updated so the caller can save), ensuring the provider
assignment is always stored; locate code in GitLabIssueProcessor.upsertIssue
(and compare with processFromSync) and either call issueRepository.save(issue)
after issue.setProvider(...) or propagate a flag to trigger save in process().
- Around line 216-229: The provider assignment via
issue.setProvider(GitProviderType.GITLAB) isn't always persisted because
issueRepository.save(issue) only runs when the boolean changed (from
updateSyncLabels/updateSyncAssignees) is true; ensure the provider change is
always saved by persisting the issue regardless of changed — either call
issueRepository.save(issue) unconditionally after setting the provider (and
after updateSyncLabels/updateSyncAssignees), or update the changed flag to
reflect the provider modification before the conditional save so
issueRepository.save(issue) always runs when the provider was set.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java`:
- Around line 490-495: The intelligence-service DB schema still defines the
organization column as githubId/github_id while the application-server uses
providerId/provider_id; update the intelligence-service schema definition for
the organization table to use providerId (provider_id) instead of githubId
(github_id) to match the Organization entity/migration, or explicitly add a
mapping/alias or migration that preserves compatibility if the difference is
intentional; ensure the schema identifier (githubId) is replaced with providerId
and any references/usages in the intelligence-service codebase are updated
accordingly.

---

Outside diff comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java`:
- Around line 144-166: The class GitHubOrganizationProcessor currently uses an
explicit constructor and a manually-created logger; replace these with Lombok
annotations by removing the explicit constructor and LoggerFactory usage and
adding `@RequiredArgsConstructor` and `@Slf4j` to the class declaration so Spring
will still inject organizationRepository and projectIntegrityService via
constructor injection and logging calls (e.g., log.info(...)) use the
Lombok-provided logger; also add the corresponding lombok imports and remove
now-unused manual logger/imports.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java`:
- Around line 259-272: In syncGitLabUserForPAT the provider constant is
incorrectly set to "GITHUB": change all provider usages in that
method—userRepository.acquireLoginLock(login, "GITHUB"),
userRepository.freeLoginConflicts(login, userInfo.id(), "GITHUB"), and
userRepository.upsertUser(..., "GITHUB")—to the GitLab provider value (e.g.
"GITLAB" or the corresponding enum/constant used elsewhere) so locks and the
upsert store the correct provider namespace and avoid cross-provider conflicts.

---

Nitpick comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java`:
- Around line 32-50: Replace the manual Logger and constructor in
GitLabIssueSyncService with Lombok annotations: add `@Slf4j` and
`@RequiredArgsConstructor` on the GitLabIssueSyncService class, remove the
explicit Logger field "log" and the explicit constructor, and keep existing
final fields (graphQlClientProvider, issueProcessor, gitLabProperties) and the
static constant GET_PROJECT_ISSUES_DOCUMENT unchanged so Lombok generates the
constructor and logging instance.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java`:
- Around line 17-30: Rename the method parameter githubId to a provider-agnostic
name (e.g., providerId or externalId) in the upsertIdentity method and update
its Javadoc to reflect provider-agnostic semantics; update all references inside
the method (calls to organizations.findByProviderId, o.setId, o.setProviderId
and any other usages) and adjust any callers of upsertIdentity to use the new
parameter name so signatures remain consistent across the multi-provider
architecture.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java`:
- Line 306: The call to organizationRepository.upsert currently passes the
hardcoded string "GITHUB"; replace this literal with the enum constant to ensure
type safety by using GitProviderType.GITHUB.name() (or
GitProviderType.GITHUB.toString() if codebase prefers) when invoking
organizationRepository.upsert so the provider type comes from the
GitProviderType enum rather than a raw string.
- Line 322: The call to organizationRepository.upsert(...) is using the string
literal "GITHUB"; change it to use the same enum constant used earlier (the one
used at line 306) for provider identification so both calls are
consistent—replace the final argument "GITHUB" in
organizationRepository.upsert(databaseId, databaseId, login, name, avatarUrl,
url, "GITHUB") with the enum constant (the same symbol used in the nearby
refactor).

In
`@server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql`:
- Around line 26-41: The GraphQL query in GetProjectIssues.graphql uses fixed
limits labels(first: 100) and assignees(first: 20) which can truncate data for
issues with more labels/assignees; update the sync logic that executes this
query (or the code that processes its results) to detect when the returned
label/assignee counts equal the limit and emit a warning log or metric
mentioning labels(first:100) or assignees(first:20), and also add a brief
comment in GetProjectIssues.graphql documenting the imposed limits and expected
behavior so maintainers know this is intentional.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java`:
- Around line 405-411: Remove the dead helper method labelNames() from
GitLabIssueMessageHandlerIntegrationTest (it is defined but never referenced);
locate the private Set<String> labelNames(Issue issue) method and delete it to
eliminate unused code and clean up the test class.
- Around line 415-454: The GitLabTestEventListener uses non-thread-safe
ArrayList fields (createdEvents, closedEvents, reopenedEvents) which can fail
under parallel event publishing; replace those ArrayList instances with
thread-safe collections (e.g., CopyOnWriteArrayList or ConcurrentLinkedQueue
wrapped as lists) and keep the existing
getCreatedEvents/getClosedEvents/getReopenedEvents to return snapshots (new
ArrayList<>(...)) and ensure clear() safely empties the thread-safe collections;
update the field initializations in class GitLabTestEventListener and any direct
operations (add/clear) to use the chosen concurrent collection API.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java`:
- Around line 119-127: Remove the unused local variable `repo` in the test
method `updateAction_routesToProcess()` by calling `setupRepository()` for its
side effects without assigning its return value (i.e., replace `Repository repo
= setupRepository();` with simply `setupRepository();`); apply the same cleanup
to the other tests mentioned (`closeAction_routesToProcessClosed`,
`reopenAction_routesToProcessReopened`, and `unknownAction_skipsProcessing`)
where `setupRepository()` is invoked but its return value is unused.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 789575d and 9f4f84b.

📒 Files selected for processing (65)
  • .graphqlrc.yml
  • docs/contributor/erd/schema.mmd
  • server/application-server/pom.xml
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/GitLabSyncConstants.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookLabel.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookProject.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/dto/GitLabWebhookUser.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/dto/GitLabIssueEventDTO.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/Organization.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/Repository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/github/GitHubUserProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceActivationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceInstallationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java
  • server/application-server/src/main/resources/db/changelog/1772200000000_changelog.xml
  • server/application-server/src/main/resources/db/master.xml
  • server/application-server/src/main/resources/graphql/gitlab/operations/GetProjectIssues.graphql
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationRepositoriesMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationTargetMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectSyncServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/subissue/github/GitHubSubIssuesMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubMembershipMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/practices/PracticesControllerIntegrationTest.java

Comment thread docs/contributor/erd/schema.mmd Outdated
Move GitProviderType enum from workspace to gitprovider.common package
to satisfy the architecture boundary rule (gitprovider must not depend
on workspace). Inline the fromGitProviderMode conversion into
Workspace.getProviderType(). Regenerate intelligence-service Drizzle
schema to reflect provider column additions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the intelligence-service TypeScript AI service: LLM orchestration, mentor chat label Feb 28, 2026
Refactor processFromSync to use SyncIssueData parameter object (was 19
params, max 6). Add OrganizationRepository.upsert to native SQL allowlist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java (1)

3-3: Redundant self-import.

GitProviderType is in the same package (de.tum.in.www1.hephaestus.gitprovider.common), so this import is unnecessary.

♻️ Suggested fix
 package de.tum.in.www1.hephaestus.gitprovider.common;
 
-import de.tum.in.www1.hephaestus.gitprovider.common.GitProviderType;
 import jakarta.persistence.Column;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java`
at line 3, Remove the redundant self-import of GitProviderType from
BaseGitServiceEntity: since GitProviderType is in the same package
(de.tum.in.www1.hephaestus.gitprovider.common), delete the import statement
referencing GitProviderType in the BaseGitServiceEntity file so the class relies
on the package-level visibility instead of importing itself.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java (1)

232-235: Optional: Consider using a collision-resistant hash for deterministic label ID generation.

The current implementation combines repositoryId and labelName.hashCode() to generate a deterministic ID. While labelName.hashCode() can produce collisions, the fallback logic using findByRepositoryIdAndName(repositoryId, title) is scoped to both the repository and label name, so the correct label will always be found despite ID collisions. The unique constraint in the database is on (repository_id, name), not on ID alone, which ensures safety.

That said, using a collision-resistant hash like MurmurHash would reduce unnecessary fallback queries and improve robustness at scale, though it is not required for correctness.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`
around lines 232 - 235, The generateDeterministicLabelId method currently mixes
repositoryId and labelName.hashCode(), which is prone to Java String hash
collisions; update BaseGitLabProcessor.generateDeterministicLabelId to compute a
collision-resistant hash of labelName (e.g., use MurmurHash3 or a SHA-256/MD5
digest and take a fixed 32-bit slice) instead of labelName.hashCode(), then
combine that 32-bit hash with repositoryId as before and return the negated
long; ensure you import and use a stable hashing utility (e.g., Guava's
Hashing.murmur3_32() or java.security.MessageDigest) so deterministic IDs are
less collision-prone.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java (1)

116-151: Add explicit @NonNull contracts to required record components.

The new sync DTO records declare nullable fields, but required fields are not annotated. This weakens null-safety at call sites and in static analysis.

💡 Proposed refactor
+import org.springframework.lang.NonNull;
 ...
-public record SyncLabelData(String globalId, String title, `@Nullable` String color) {}
+public record SyncLabelData(`@NonNull` String globalId, `@NonNull` String title, `@Nullable` String color) {}

 public record SyncAssigneeData(
-    String globalId,
-    String username,
+    `@NonNull` String globalId,
+    `@NonNull` String username,
     `@Nullable` String name,
     `@Nullable` String avatarUrl,
     `@Nullable` String webUrl
 ) {}

 public record SyncIssueData(
-    String globalId,
-    String iid,
-    String title,
+    `@NonNull` String globalId,
+    `@NonNull` String iid,
+    `@NonNull` String title,
     `@Nullable` String description,
-    String state,
+    `@NonNull` String state,
     boolean confidential,
-    String webUrl,
+    `@NonNull` String webUrl,
     `@Nullable` String createdAt,
     ...
 ) {}

As per coding guidelines server/application-server/**/*.java: Use Java records for DTOs with @NonNull annotation on required fields.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`
around lines 116 - 151, The record components for SyncLabelData,
SyncAssigneeData, and SyncIssueData lack explicit `@NonNull` annotations for
fields that must never be null (e.g., SyncLabelData.globalId, title;
SyncAssigneeData.globalId, username; SyncIssueData.globalId, iid, title, state,
confidential, webUrl, commentsCount, etc.). Update each record declaration to
annotate all required components with `@NonNull` while keeping the existing
`@Nullable` annotations for optional fields so static analysis and callers get
correct null contracts; ensure imports for the `@NonNull` annotation are added
where missing and that only truly optional components remain annotated
`@Nullable`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java`:
- Around line 128-132: totalSynced is incremented for every node even when
processIssueNode (and similar processFromSync) skip and return null, causing
overcounting; change the loop to capture the return value of
processIssueNode(issueNode, repository) (and analogous calls to processFromSync)
and only increment totalSynced when the returned object is non-null (or a
boolean "synced" indicator is true), so skipped/confidential/invalid issues are
not counted.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java`:
- Around line 123-127: The test uses a shared singleton GitLabTestEventListener
(referenced as eventListener) and calls eventListener.clear() in setUp(), which
is flaky under parallel tests; replace the shared mutable listener usage with
per-test event capture using Spring's ApplicationEvents (inject
org.springframework.boot.test.system.ApplicationEvents or
org.springframework.context.ApplicationEventPublisher in the test) and update
all places that read from GitLabTestEventListener (including methods in
GitLabIssueMessageHandlerIntegrationTest and related tests around lines 415-453)
to assert events from the per-test ApplicationEvents instance instead of
mutating/clearing the singleton; remove eventListener.clear() from setUp() and
any reliance on GitLabTestEventListener state so tests are isolated and safe for
parallel execution.

---

Nitpick comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java`:
- Line 3: Remove the redundant self-import of GitProviderType from
BaseGitServiceEntity: since GitProviderType is in the same package
(de.tum.in.www1.hephaestus.gitprovider.common), delete the import statement
referencing GitProviderType in the BaseGitServiceEntity file so the class relies
on the package-level visibility instead of importing itself.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`:
- Around line 232-235: The generateDeterministicLabelId method currently mixes
repositoryId and labelName.hashCode(), which is prone to Java String hash
collisions; update BaseGitLabProcessor.generateDeterministicLabelId to compute a
collision-resistant hash of labelName (e.g., use MurmurHash3 or a SHA-256/MD5
digest and take a fixed 32-bit slice) instead of labelName.hashCode(), then
combine that 32-bit hash with repositoryId as before and return the negated
long; ensure you import and use a stable hashing utility (e.g., Guava's
Hashing.murmur3_32() or java.security.MessageDigest) so deterministic IDs are
less collision-prone.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`:
- Around line 116-151: The record components for SyncLabelData,
SyncAssigneeData, and SyncIssueData lack explicit `@NonNull` annotations for
fields that must never be null (e.g., SyncLabelData.globalId, title;
SyncAssigneeData.globalId, username; SyncIssueData.globalId, iid, title, state,
confidential, webUrl, commentsCount, etc.). Update each record declaration to
annotate all required components with `@NonNull` while keeping the existing
`@Nullable` annotations for optional fields so static analysis and callers get
correct null contracts; ensure imports for the `@NonNull` annotation are added
where missing and that only truly optional components remain annotated
`@Nullable`.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f4f84b and a5d4634.

📒 Files selected for processing (18)
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProviderType.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/GitProviderType.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/Workspace.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceActivationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/dto/WorkspaceDTO.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/dto/WorkspaceListItemDTO.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/architecture/CodeQualityTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/GitProviderTypeTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceControllerIntegrationTest.java
  • server/intelligence-service/src/shared/db/schema.ts
💤 Files with no reviewable changes (1)
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/GitProviderType.java
✅ Files skipped from review due to trivial changes (1)
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceControllerIntegrationTest.java

FelixTJDietrich and others added 4 commits February 28, 2026 14:13
Replace 1772200000000 (fake timestamp) with 1772284124265 (real epoch
millis). Renumber changesets sequentially (1-23, no gaps). Content is
identical — same 14 provider columns, 1 rename, 8 constraint updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nd native_id

Replace enum-based GitProviderType with GitProvider entity FK on all
BaseGitServiceEntity subclasses. Switch from provider-native IDs as
primary keys (with negation for GitLab) to auto-generated synthetic PKs
paired with a native_id column. Update all processors, repositories,
sync services, message handlers, and tests to use the new
(provider_id, native_id) composite uniqueness model.
- Fix GitLab group token auth: add fallback to group API when /user
  endpoint fails, supporting group/project access tokens
- Fix JPA PK vs nativeId mismatch: upsertUser returns nativeId but
  callers need JPA PK — now resolved via findByLogin after upsert
- Fix GitProvider auto-creation for self-hosted GitLab instances
  (e.g. gitlab.lrz.de) not seeded by Liquibase
- Remove @transactional from ensureGitHubAppInstallations to prevent
  UnexpectedRollbackException when individual installations fail
- Apply formatting to code from prior synthetic PK refactor
…y upsert race

- Return SyncResult from GitLabIssueSyncService.syncIssues() to track
  completion status (COMPLETED, ABORTED_RATE_LIMIT, ABORTED_ERROR)
- Use Repository.lastSyncAt with 5-minute safety buffer as updatedAfter
  cursor for GitLab issue sync, avoiding full re-sync on every restart
- Add RepositoryRepository.updateLastSyncAt() to persist sync timestamps
  only on successful completion
- Replace find-or-create pattern in WorkspaceRepositoryMonitorService with
  native SQL INSERT ... ON CONFLICT DO UPDATE upsert, eliminating
  ObjectOptimisticLockingFailureException from concurrent NATS events
- Fix incorrect repo.setId(snapshot.id()) that used GitHub native ID as
  synthetic PK, and missing nativeId/provider fields
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/settings/WorkspaceTeamSettingsControllerIntegrationTest.java (1)

770-800: ⚠️ Potential issue | 🟠 Major

Set provider on Team/Repository fixtures before save.

Lines 772 and 792 now set native IDs, but provider is still unset. With provider-scoped schema, these saves can fail or drift from production behavior.

🧩 Suggested fix in test helpers
     private Team createTeam(String name, String organization) {
         Team newTeam = new Team();
         newTeam.setNativeId(entityIdGenerator.incrementAndGet());
+        newTeam.setProvider(workspace.getOrganization().getProvider());
         newTeam.setName(name);
         newTeam.setOrganization(organization);
         newTeam.setDescription("Test team: " + name);
         newTeam.setHtmlUrl("https://github.com/orgs/" + organization + "/teams/" + name);
         newTeam.setPrivacy(Team.Privacy.VISIBLE);
         return teamRepository.save(newTeam);
     }

     private Repository createRepository(String nameWithOwner) {
         String[] parts = nameWithOwner.split("/");
         String repoName = parts.length > 1 ? parts[1] : nameWithOwner;

         Repository repo = new Repository();
         repo.setNativeId(entityIdGenerator.incrementAndGet());
+        repo.setProvider(workspace.getOrganization().getProvider());
         repo.setName(repoName);
         repo.setNameWithOwner(nameWithOwner);
         repo.setHtmlUrl("https://github.com/" + nameWithOwner);
         repo.setDescription("Test repository: " + nameWithOwner);
         repo.setPushedAt(Instant.now());
         repo.setCreatedAt(Instant.now());
         repo.setUpdatedAt(Instant.now());
         return repositoryRepository.save(repo);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/settings/WorkspaceTeamSettingsControllerIntegrationTest.java`
around lines 770 - 800, The Team and Repository test fixtures don't set the
provider, which can break provider-scoped schema checks; update createTeam and
createRepository to set the provider on the entity before saving (e.g. call
newTeam.setProvider(Provider.GITHUB) in createTeam and
repo.setProvider(Provider.GITHUB) in createRepository, using the project's
Provider enum or appropriate constant) so the saved fixtures reflect production
provider scoping.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java (1)

860-872: ⚠️ Potential issue | 🟠 Major

Native-id migration is incomplete in this test setup.

Line 861 duplicates setNativeId, and the class still reads entities via findById(PR_26_ID) / getId() semantics. With synthetic PKs, these assertions become unstable and can hide regressions.

✅ Suggested direction
-        org.setNativeId(FIXTURE_ORG_ID);
-        org.setNativeId(FIXTURE_ORG_ID);
+        org.setNativeId(FIXTURE_ORG_ID);
- PullRequest pr = pullRequestRepository.findById(PR_26_ID).orElseThrow();
- assertThat(pr.getId()).isEqualTo(PR_26_ID);
+ PullRequest pr = pullRequestRepository
+     .findByNativeIdAndProviderId(PR_26_ID, githubProviderId)
+     .orElseThrow();
+ assertThat(pr.getNativeId()).isEqualTo(PR_26_ID);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java`
around lines 860 - 872, Remove the duplicate org.setNativeId call and complete
the native-id migration for repository/test fixtures: set
testRepository.setNativeId(FIXTURE_REPO_ID), associate it with the saved
organization (testRepository.setOrganization(org)), persist it with
repositoryRepository.save(testRepository), and then update any assertions or
lookups that use findById(PR_26_ID) / getId() to instead use the native-id API
(e.g., findByNativeId(FIXTURE_REPO_ID)) or use the saved entity's returned getId
consistently so the test no longer relies on unstable synthetic PK semantics.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamProcessor.java (1)

45-52: ⚠️ Potential issue | 🟠 Major

Natural-key lookups are not provider-scoped.

Line 46 and Line 198 query by organization+name only. In multi-provider setups, this can bind to the wrong team record even though the entity now carries provider. Scope these lookups by providerId as well.

💡 Proposed fix
-            existingTeam = teamRepository.findByOrganizationIgnoreCaseAndName(orgLogin, dto.name());
+            existingTeam = teamRepository.findByOrganizationIgnoreCaseAndNameAndProviderId(
+                orgLogin,
+                dto.name(),
+                context.providerId()
+            );
...
-        Optional<Team> conflicting = teamRepository.findByOrganizationIgnoreCaseAndName(orgLogin, teamName);
+        Optional<Team> conflicting = teamRepository.findByOrganizationIgnoreCaseAndNameAndProviderId(
+            orgLogin,
+            teamName,
+            context.providerId()
+        );

Also applies to: 198-199

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamProcessor.java`
around lines 45 - 52, The natural-key lookup in GitHubTeamProcessor uses
teamRepository.findByOrganizationIgnoreCaseAndName(orgLogin, dto.name()) without
scoping by providerId, which can return the wrong team in multi-provider setups;
update the lookup to include context.providerId() (e.g. call or add a repository
method like findByOrganizationIgnoreCaseAndNameAndProviderId and pass orgLogin,
dto.name(), context.providerId()) and do the same change for the second
occurrence (the similar call around the later block that currently queries by
organization+name) so both natural-key lookups are provider-scoped while leaving
the nativeId+providerId fallback unchanged.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/commit/github/CommitAuthorEnrichmentService.java (1)

114-180: ⚠️ Potential issue | 🟠 Major

Provider scoping is missing from author/committer resolution in multi-provider scenarios.

providerId is passed into enrichCommitAuthors() and threaded to upsertUsers(), but CommitAuthorResolver.resolveByEmail() and resolveByLogin() lack provider context. In multi-provider mode, overlapping logins/emails can incorrectly link commits to users from other providers. Update CommitAuthorResolver to accept and enforce providerId in both resolution methods, and modify underlying UserRepository queries to filter by provider.

Applies to: lines 114–180, 206, 215, 303, 323, and 660–671.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/commit/github/CommitAuthorEnrichmentService.java`
around lines 114 - 180, Commit resolution currently ignores provider scoping
which can link commits to users from other providers; update
CommitAuthorResolver.resolveByEmail(...) and
CommitAuthorResolver.resolveByLogin(...) to accept a Long providerId parameter
and enforce it when querying users, and update the underlying UserRepository
methods used by those resolvers to include providerId filtering (e.g., add
providerId predicates to methods called from CommitAuthorResolver and any
findByEmail/findByLogin queries), then propagate providerId from
enrichCommitAuthors(...) (where providerId is already available) into calls to
these resolver methods and ensure upsertUsers(...) still receives providerId.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessorIntegrationTest.java (1)

117-140: ⚠️ Potential issue | 🔴 Critical

Use testRepository.getId() instead of FIXTURE_REPO_ID for repository FK calls.

Repository.id is a synthetic auto-generated primary key, distinct from nativeId (the provider's original ID). The test correctly seeds nativeId with FIXTURE_REPO_ID at line 129, but subsequent calls to upsertCore() and findByRepositoryIdAndNumber() at lines 415, 425, 426, and 439 incorrectly pass FIXTURE_REPO_ID as the repositoryId parameter. These methods expect the synthetic database ID, so they should use testRepository.getId(). Additionally, line 469 compares result.getRepository().getId() (synthetic ID) against FIXTURE_REPO_ID (nativeId), which will always fail since they are different values.

Replace FIXTURE_REPO_ID with testRepository.getId() in all repository FK operations within the test setup and assertions.

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessorIntegrationTest.java`
around lines 117 - 140, The test uses the provider native ID (FIXTURE_REPO_ID)
where the database synthetic primary key is required; update all repository FK
usages—calls to upsertCore(..., repositoryId, ...),
findByRepositoryIdAndNumber(...), and the assertion comparing
result.getRepository().getId()—to use testRepository.getId() instead of
FIXTURE_REPO_ID so the repositoryId parameter and assertions reference the
entity's synthetic DB id; verify every occurrence in
GitHubPullRequestProcessorIntegrationTest (including the calls near lines
referencing upsertCore and findByRepositoryIdAndNumber and the final ID
comparison) is replaced accordingly.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceInstallationService.java (1)

523-539: ⚠️ Potential issue | 🟠 Major

Use provider-scoped user lookup in ownership sync.

Line 531 and Line 579 read users via findByLogin(...) without provider context. With provider-scoped identity, the same login can exist on multiple providers, which can assign workspace ownership to the wrong user or read back the wrong JPA PK after upsert.

🔧 Proposed fix
- var existingUser = userRepository.findByLogin(accountLogin);
+ var existingUser = userRepository.findByLoginAndProviderId(accountLogin, providerId);

...
- return userRepository
-     .findByLogin(accountLogin)
+ return userRepository
+     .findByLoginAndProviderId(accountLogin, providerId)
      .map(User::getId)
      .orElseThrow(() -> new IllegalStateException("User not found after upsert: login=" + accountLogin));

Also applies to: 578-581

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceInstallationService.java`
around lines 523 - 539, syncGitHubUserForOwnership is using
userRepository.findByLogin(accountLogin) which ignores provider-scoped identity
and can return the wrong user; change the lookup and any subsequent upsert to
use provider-scoped methods (e.g., findByLoginAndProvider or equivalent) and
include the provider identifier when querying/creating the user so the same
accountLogin on different providers is distinguished; update both the initial
existence check in syncGitHubUserForOwnership and the later read/upsert path
referenced around the later findByLogin call (lines ~578-581) to pass the
provider context and return the correct JPA id.
♻️ Duplicate comments (4)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java (1)

133-137: ⚠️ Potential issue | 🟠 Major

totalSynced is still inflated by skipped issues.

Line 136 increments for every node, but issueProcessor.processFromSync(...) can skip (e.g., confidential/invalid) without throwing. Count should increase only when an issue is actually processed.

🐛 Proposed fix
-                for (Map<String, Object> issueNode : nodes) {
+                for (Map<String, Object> issueNode : nodes) {
                     try {
-                        processIssueNode(issueNode, repository);
-                        totalSynced++;
+                        if (processIssueNode(issueNode, repository)) {
+                            totalSynced++;
+                        }
                     } catch (Exception e) {
                         log.warn(
                             "Error processing issue: projectPath={}, issueId={}",
                             safeProjectPath,
                             issueNode.get("iid"),
                             e
                         );
                     }
                 }
@@
-    private void processIssueNode(Map<String, Object> node, Repository repository) {
+    private boolean processIssueNode(Map<String, Object> node, Repository repository) {
@@
-        issueProcessor.processFromSync(syncData, repository);
+        return issueProcessor.processFromSync(syncData, repository) != null;
     }

Also applies to: 198-288

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java`
around lines 133 - 137, The totalSynced counter is incremented for every node
even when processIssueNode/issueProcessor.processFromSync skips an issue without
throwing; change the logic so totalSynced only increments when an issue was
actually processed: modify processIssueNode (or the internal call to
issueProcessor.processFromSync) to return a boolean (e.g., true when processed,
false when skipped) and in the caller loops (the for-loop that currently calls
processIssueNode(...) and the similar block around lines 198-288) increment
totalSynced only when that boolean is true; alternatively, have processIssueNode
throw a specific exception on skip and catch only real errors—ensure totalSynced
update is moved inside the successful-processing branch rather than
unconditionally after the call.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java (1)

129-133: ⚠️ Potential issue | 🟠 Major

Replace shared singleton event collection with per-test capture.

Line 132 clears state on a singleton listener, and Lines 435-473 keep mutable ArrayList state in a shared bean. This can interleave tests under parallel execution/context reuse and make assertions flaky. Prefer per-test event capture (e.g., ApplicationEvents) instead of shared mutable listener state.

As per coding guidelines server/application-server/src/test/**/*.java: "Test cases may run in parallel, so avoid required cleanup steps and assume that there might be data from previous tests in the database."

Also applies to: 435-473

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java`
around lines 129 - 133, The test uses a shared singleton listener
(eventListener) with mutable ArrayList state cleared in setUp(), which makes
tests flaky under parallel runs; change to per-test event capture by replacing
the shared bean usage with a test-scoped capture (e.g., inject or use Spring's
ApplicationEvents or create a fresh instance of the listener per test) and
remove reliance on clearing the singleton; specifically update
GitLabIssueMessageHandlerIntegrationTest.setUp() to instantiate or wire a
per-test listener instead of calling eventListener.clear(), and update the
shared listener implementation (the class holding the mutable ArrayList
referenced in the diff around lines 435-473) to not keep global mutable state so
assertions read events from the per-test ApplicationEvents or the newly created
per-test listener instance.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java (2)

227-240: ⚠️ Potential issue | 🟠 Major

Provider persistence is now handled correctly.

Looking at the flow:

  1. Line 233: issue.setProvider(repository.getProvider())
  2. Lines 236-240: issueRepository.save(issue) is called if changed is true

However, if no labels or assignees changed (changed == false), the provider won't be persisted. The upsertCore at line 202 uses native SQL and doesn't persist the provider field (which is a JPA relationship).

This was flagged in past review comments but appears not fully addressed. The provider should always be saved after being set.

🐛 Proposed fix: Always save after setting provider
         issue.setProvider(repository.getProvider());

         // Resolve and persist labels/assignees within this transaction
         boolean changed = updateSyncLabels(data.syncLabels(), issue.getLabels(), repository);
         changed |= updateSyncAssignees(data.syncAssignees(), issue.getAssignees(), providerId);
-        if (changed) {
-            issue = issueRepository.save(issue);
-        }
+        // Always save to persist provider and any relationship changes
+        issue = issueRepository.save(issue);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`
around lines 227 - 240, In GitLabIssueProcessor, after calling
issue.setProvider(repository.getProvider()) ensure the provider relationship is
always persisted: remove the conditional-only save and call
issueRepository.save(issue) unconditionally (or otherwise persist the provider)
so the provider field set after upsertCore is always stored; keep the existing
updateSyncLabels/updateSyncAssignees logic (updateSyncLabels,
updateSyncAssignees) but do not rely on their resulting "changed" flag to
determine whether to persist the provider.

344-361: ⚠️ Potential issue | 🟠 Major

Same issue in webhook path: Provider may not persist.

In upsertIssue(), the provider is set at line 347 (issue.setProvider(repository.getProvider())), but the issue is not saved after this. The caller process() only saves if labels/assignees changed. If there are no relationship changes, the provider won't be persisted.

🐛 Proposed fix: Save after setting provider in upsertIssue
         if (issue != null) {
             issue.setProvider(repository.getProvider());
+            issue = issueRepository.save(issue);

             if (isNew) {
                 eventPublisher.publishEvent(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`
around lines 344 - 361, The issue is that upsertIssue() sets the provider via
issue.setProvider(repository.getProvider()) but does not persist the change, so
callers like process() that only save on label/assignee changes won't persist
the provider; fix by saving the entity after setting the provider (e.g., call
issueRepository.save(issue) or issueRepository.saveAndFlush(issue)) inside
upsertIssue() immediately after issue.setProvider(...), keeping the existing
event publication logic (IssueCreated event) intact and returning the saved
Issue.
🧹 Nitpick comments (11)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/IssueRepository.java (1)

80-91: Orphaned Javadoc block for upsertCore.

The Javadoc at lines 80-91 documents the upsertCore method but is separated from it by findByIdWithBlockedBy and its Javadoc. Consider moving this Javadoc directly above the upsertCore method (line 112) for proper association.

📝 Suggested fix: move Javadoc to correct location

Remove the Javadoc block at lines 80-91 and place it directly above the @Modifying annotation at line 112:

-    /**
-     * Atomically inserts or updates an issue's core fields (race-condition safe).
-     * <p>
-     * Uses PostgreSQL's ON CONFLICT to handle concurrent inserts on the unique constraint
-     * (repository_id, number). This eliminates the race condition where two threads both
-     * pass the findById check and try to insert the same issue.
-     * <p>
-     * <b>Note:</b> This only handles scalar fields and FK references. ManyToMany relationships
-     * (labels, assignees) must be handled separately after calling this method.
-     *
-     * `@return` 1 if inserted, 1 if updated (always 1 on success due to DO UPDATE)
-     */
     /**
      * Finds an issue by ID with its blockedBy collection eagerly loaded.
      ...
     */
     Optional<Issue> findByIdWithBlockedBy(`@Param`("id") Long id);

+    /**
+     * Atomically inserts or updates an issue's core fields (race-condition safe).
+     * ...
+     * `@return` 1 if inserted, 1 if updated (always 1 on success due to DO UPDATE)
+     */
     `@Modifying`(flushAutomatically = true, clearAutomatically = true)
     `@Transactional`
     `@Query`(...)
     int upsertCore(...);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/IssueRepository.java`
around lines 80 - 91, Move the orphaned Javadoc that describes upsertCore so it
directly precedes the upsertCore method declaration: remove the Javadoc block
currently placed above findByIdWithBlockedBy and insert it immediately above the
`@Modifying` / upsertCore method (reference symbol: upsertCore; the intervening
method: findByIdWithBlockedBy). Ensure the Javadoc stays intact (including notes
about ON CONFLICT and ManyToMany handling) and is attached to the upsertCore
method so IDEs and javadocs associate it correctly.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewProcessor.java (1)

342-343: Bind stub PR provider from the repository, not from context.

Line 343 should use the already-resolved repository as source of truth (repository.getProvider()), so stub PRs can’t be persisted with a null/mismatched provider when context is partially populated.

Proposed patch
         PullRequest pr = new PullRequest();
         pr.setNativeId(prId);
-        pr.setProvider(context.provider());
+        pr.setProvider(repository.getProvider());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewProcessor.java`
around lines 342 - 343, Replace the provider assignment to use the repository's
resolved provider rather than the partially-populated context: in
GitHubPullRequestReviewProcessor locate the stub PR population where
pr.setProvider(context.provider()) is called and change it to use
repository.getProvider() so stub PRs are persisted with the repository's
provider; ensure pr.setNativeId(prId) remains unchanged and that repository is
the same Repository instance used earlier in this method.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/PullRequestReviewCommentRepository.java (1)

11-15: Add @Repository annotation to this repository interface.

The new methods are good, but this interface should explicitly carry @Repository for guideline consistency.

As per coding guidelines "Use @Service annotation for business logic classes, @Repository for data access classes extending JpaRepository, and @RestController for REST endpoints".

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/PullRequestReviewCommentRepository.java`
around lines 11 - 15, Add the Spring stereotype to the repository interface
PullRequestReviewCommentRepository by annotating the interface with `@Repository`;
update the declaration of PullRequestReviewCommentRepository (which declares
findByNativeIdAndProviderId and existsByNativeIdAndProviderId) to include
`@Repository` so the interface is explicitly marked as a data access component per
project guidelines.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabPushMessageHandler.java (1)

51-66: Prefer @RequiredArgsConstructor for constructor injection consistency.

This handler now has multiple final dependencies; switching to Lombok constructor generation would align with project conventions and reduce boilerplate.

As per coding guidelines, server/application-server/**/*.java: “Use constructor injection via @RequiredArgsConstructor annotation”.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabPushMessageHandler.java`
around lines 51 - 66, Replace the manual constructor boilerplate with Lombok by
marking the handler's dependency fields as final and annotating the class with
`@RequiredArgsConstructor`, then remove the current explicit
GitLabPushMessageHandler(...) constructor; to preserve the required superclass
call to super(GitLabPushEventDTO.class, deserializer, transactionTemplate) add a
minimal explicit constructor that only takes NatsMessageDeserializer and
TransactionTemplate and calls super(...) while letting Lombok inject the other
final fields—this keeps constructor injection consistent (update fields:
projectProcessor, organizationRepository, repositoryRepository,
gitProviderRepository, gitLabProperties to final and add
`@RequiredArgsConstructor` to the class, keep a small explicit ctor that forwards
deserializer and transactionTemplate to super).
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java (1)

79-97: Consider converting this expanded constructor to @RequiredArgsConstructor.

The constructor wiring changed in this PR; moving to Lombok required-args injection would align this service with project conventions and reduce maintenance overhead.

As per coding guidelines: server/application-server/**/*.java: Use constructor injection via @RequiredArgsConstructor annotation.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java`
around lines 79 - 97, Replace the explicit constructor in
GitHubOrganizationSyncService with Lombok's constructor injection by annotating
the class with `@RequiredArgsConstructor`, making all injected fields
(graphQlClientProvider, organizationProcessor, userProcessor,
organizationMembershipRepository, syncProperties, exceptionClassifier,
graphQlSyncHelper, gitProviderRepository) final, removing the manual
constructor, and adding the lombok.RequiredArgsConstructor import; ensure no
other initialization logic in the removed constructor is lost.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/sync/GitHubDataSyncService.java (1)

120-176: Prefer @RequiredArgsConstructor over a growing manual constructor.

This constructor has expanded again; migrating this service to Lombok required-args injection will reduce wiring drift and keep consistency with project conventions.

As per coding guidelines: server/application-server/**/*.java: Use constructor injection via @RequiredArgsConstructor annotation.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/sync/GitHubDataSyncService.java`
around lines 120 - 176, Convert GitHubDataSyncService to use Lombok-required
args: make all injected fields (syncSchedulerProperties, gitProviderRepository,
syncTargetProvider, organizationMembershipListener, repositoryRepository,
organizationRepository, labelSyncService, milestoneSyncService,
issueSyncService, issueDependencySyncService, issueTypeSyncService,
subIssueSyncService, pullRequestSyncService, discussionSyncService,
teamSyncService, projectSyncService, organizationSyncService,
repositorySyncService, collaboratorSyncService, commitBackfillService,
commitAuthorEnrichmentService, commitMetadataEnrichmentService,
exceptionClassifier, tokenProvider, gitHubAppTokenService, rateLimitTracker,
monitoringExecutor) final, remove the long manual constructor, and annotate the
class GitHubDataSyncService with `@RequiredArgsConstructor`; preserve the
`@Qualifier`("monitoringExecutor") annotation on the monitoringExecutor field so
Lombok's constructor keeps the qualifier. Ensure imports for
lombok.RequiredArgsConstructor are added.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/RepositoryRepository.java (1)

94-98: Refresh updated_at in the conflict-update branch.

The conflict path updates mutable snapshot fields but leaves updated_at unchanged, which can make recently updated rows look stale in diagnostics.

♻️ Proposed tweak
         ON CONFLICT (provider_id, name_with_owner) DO UPDATE SET
             name = EXCLUDED.name,
             is_private = EXCLUDED.is_private,
-            organization_id = COALESCE(repository.organization_id, EXCLUDED.organization_id)
+            organization_id = COALESCE(repository.organization_id, EXCLUDED.organization_id),
+            updated_at = NOW()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/RepositoryRepository.java`
around lines 94 - 98, The ON CONFLICT DO UPDATE branch in RepositoryRepository
(the SQL string that performs INSERT ... ON CONFLICT (provider_id,
name_with_owner) DO UPDATE SET ...) fails to refresh updated_at; modify that
UPDATE clause (in the SQL constant or method building the statement inside
RepositoryRepository.java) to set updated_at = EXCLUDED.updated_at or updated_at
= CURRENT_TIMESTAMP (choose consistent convention used elsewhere) so the row’s
timestamp reflects the conflict-path update.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java (1)

35-51: Align service wiring with project Lombok conventions.

This class uses manual LoggerFactory and an explicit constructor. The application-server convention is @Slf4j and @RequiredArgsConstructor for service wiring.

As per coding guidelines server/application-server/**/*.java: "Use constructor injection via @RequiredArgsConstructor annotation" and "Use Lombok annotations: @RequiredArgsConstructor, @Slf4j, @Builder, @Getter, @Setter; avoid @Data in favor of granular annotations."

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java`
around lines 35 - 51, Replace the manual LoggerFactory and explicit constructor
in GitLabIssueSyncService with Lombok annotations: remove the private static
final Logger log = LoggerFactory.getLogger(GitLabIssueSyncService.class) and
delete the explicit public GitLabIssueSyncService(...) constructor, add `@Slf4j`
and `@RequiredArgsConstructor` on the GitLabIssueSyncService class, keep the
existing final fields (graphQlClientProvider, issueProcessor, gitLabProperties)
and constants (e.g., GET_PROJECT_ISSUES_DOCUMENT) as-is, and add imports for
lombok.RequiredArgsConstructor and lombok.extern.slf4j.Slf4j.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupSyncServiceTest.java (1)

121-122: Assert the concrete provider ID instead of wildcard matching.

Using anyLong() here makes provider propagation regressions invisible. Prefer eq(TEST_PROVIDER_ID) for provider-scoped calls.

Suggested tightening
- when(groupProcessor.process(any(GitLabGroupResponse.class), anyLong())).thenReturn(org);
+ when(groupProcessor.process(any(GitLabGroupResponse.class), eq(TEST_PROVIDER_ID))).thenReturn(org);

- when(groupProcessor.process(eq(subGroupResponse), anyLong())).thenReturn(subOrg);
+ when(groupProcessor.process(eq(subGroupResponse), eq(TEST_PROVIDER_ID))).thenReturn(subOrg);

As per coding guidelines: "Focus on risk: cover critical flows and edge cases first when writing tests."

Also applies to: 215-216, 232-233, 251-252, 283-284, 310-311, 335-336, 391-392

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupSyncServiceTest.java`
around lines 121 - 122, Replace the use of the wildcard anyLong() in the mocked
groupProcessor.process(...) calls with an exact match for the test provider ID
to prevent regressions: update the when(...) stubs in GitLabGroupSyncServiceTest
(calls to groupProcessor.process(GitLabGroupResponse, long)) to use
eq(TEST_PROVIDER_ID) instead of anyLong() (e.g., change
when(groupProcessor.process(any(GitLabGroupResponse.class), anyLong())) to
when(groupProcessor.process(any(GitLabGroupResponse.class),
eq(TEST_PROVIDER_ID)))); apply the same replacement at the other occurrences
mentioned (the other when(...) stubs around the test: the pairs at the noted
locations) so provider-scoped interactions are asserted against the concrete
TEST_PROVIDER_ID.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProvider.java (1)

53-55: Normalize serverUrl before persisting to avoid semantic duplicates.

Without normalization, URLs differing only by trailing slash/casing can represent duplicate providers and break exact-match lookups.

Suggested normalization
     public GitProvider(GitProviderType type, String serverUrl) {
         this.type = type;
-        this.serverUrl = serverUrl;
+        this.serverUrl = normalizeServerUrl(serverUrl);
     }
+
+    public void setServerUrl(String serverUrl) {
+        this.serverUrl = normalizeServerUrl(serverUrl);
+    }
+
+    private static String normalizeServerUrl(String value) {
+        if (value == null) {
+            return null;
+        }
+        String normalized = value.trim();
+        while (normalized.endsWith("/")) {
+            normalized = normalized.substring(0, normalized.length() - 1);
+        }
+        return normalized;
+    }

Also applies to: 60-63

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProvider.java`
around lines 53 - 55, Normalize serverUrl before persisting to avoid semantic
duplicates by adding normalization logic to GitProvider: implement a
setServerUrl(String) that normalizes input (trim, canonicalize scheme/host to
lowercase, remove any trailing slash) and/or add a `@PrePersist/`@PreUpdate method
(e.g., normalizeUrls or normalizeServerUrl) that applies the same normalization
to serverUrl (and the other URL field(s) declared in this class) so that stored
values are canonical and exact-match lookups succeed.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java (1)

232-235: Deterministic label ID generation is correct but consider edge cases.

The formula (repositoryId << 32) | (labelName.hashCode() & 0xFFFFFFFFL) and negation produces unique IDs per repository-label combination. However, String.hashCode() can produce collisions for different strings.

For labels, this is a minor risk since:

  1. Label names within a single repository are typically unique
  2. Collision would only cause a lookup by name to return the correct label anyway

If you want stronger guarantees, consider using a hash function with lower collision probability (e.g., MurmurHash).

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`
around lines 232 - 235, The current generateDeterministicLabelId method uses
String.hashCode() (in combined = (repositoryId << 32) | (labelName.hashCode() &
0xFFFFFFFFL)) which has higher collision probability; replace
labelName.hashCode() with a stronger deterministic hash (e.g., MurmurHash3 or a
truncated SHA-256) to reduce collisions while preserving the 32-bit slot
semantics, e.g., compute a 32-bit unsigned hash from labelName via your chosen
hash utility and use that value in place of labelName.hashCode(), keep the
combined bit-twiddling and the final negation to preserve existing ID sign and
layout in generateDeterministicLabelId.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23e6f46 and 9ae64ac.

📒 Files selected for processing (120)
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/commit/github/CommitAuthorEnrichmentService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProvider.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProviderRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/ProcessingContext.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/github/BaseGitHubProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/GitLabSyncConstants.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/discussion/DiscussionRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/DiscussionCommentRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationTargetMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/IssueRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/IssueCommentRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/milestone/MilestoneRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/Organization.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/OrganizationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/ProjectItemRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/ProjectRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/ProjectStatusUpdateRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/PullRequestRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/PullRequestReviewCommentRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/PullRequestReviewThreadRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/Repository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/RepositoryRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/collaborator/github/GitHubCollaboratorSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubRepositorySyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabPushMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/sync/GitHubDataSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/TeamRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubMembershipMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/User.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/UserRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/github/GitHubUserProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceActivationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceInstallationService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryMonitorService.java
  • server/application-server/src/main/resources/db/changelog/1772284124265_changelog.xml
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/activity/ActivityEventServiceIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/architecture/CodeQualityTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/architecture/DataIsolationArchitectureTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/commit/github/CommitAuthorEnrichmentServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussion/github/GitHubDiscussionProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/AbstractGitHubLiveSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLiveIssueSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLiveLabelSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLiveMilestoneSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLivePullRequestSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationRepositoriesMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationTargetMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/github/GitHubIssueProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuecomment/github/GitHubIssueCommentProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issuedependency/github/GitHubIssueDependencySyncServiceIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/milestone/github/GitHubMilestoneProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupSyncServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectSyncServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreview/github/GitHubPullRequestReviewMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectSyncServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabPushMessageHandlerTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/subissue/github/GitHubSubIssuesMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubMembershipMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/practices/PracticesControllerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/testconfig/TestUserConfig.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/testconfig/TestUserFactory.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/AbstractWorkspaceIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceMembershipControllerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceProvisioningServiceTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/settings/WorkspaceTeamSettingsControllerIntegrationTest.java
🚧 Files skipped from review as they are similar to previous changes (25)
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/GitProviderRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandler.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLivePullRequestSyncIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationRepositoriesMessageHandlerIntegrationTest.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/PullRequestReviewThreadRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/ProjectItemRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadMessageHandler.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/organization/gitlab/GitLabGroupProcessorTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/subissue/github/GitHubSubIssuesMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/github/GitHubLiveLabelSyncIntegrationTest.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/ProcessingContext.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/GitLabSyncConstants.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/discussioncomment/github/GitHubDiscussionCommentProcessorIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessorTest.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/organization/github/GitHubOrganizationMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/BaseGitServiceEntity.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/practices/PracticesControllerIntegrationTest.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/user/User.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/architecture/CodeQualityTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/label/github/GitHubLabelProcessorIntegrationTest.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectProcessor.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/gitlab/GitLabProjectSyncServiceTest.java

- Add ProcessingContext.forWebhook overload for non-repository-scoped events
- Fix 4 message handlers (Project, ProjectItem, ProjectStatusUpdate, Team)
  to resolve GitProvider from DB instead of passing null repository
- Fix PullRequestReviewThreadProcessor to use findByNativeIdAndProviderId
  instead of findById with native IDs
- Fix all integration tests to seed GitProvider and use nativeId lookups
- Regenerate ERD docs and intelligence-service DB models
- Add GitLab Issue sync implementation (processor, sync service, test)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

♻️ Duplicate comments (1)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryMonitorService.java (1)

468-476: ⚠️ Potential issue | 🟠 Major

Avoid repeated provider lookups inside snapshot processing loops.

Line 686 performs the same provider lookup for every snapshot processed at Line 468. This adds avoidable DB load on provisioning hot paths.

♻️ Suggested refactor
-        allowedSnapshots.forEach(snapshot -> {
+        GitProvider githubProvider = resolveGitHubProvider();
+        allowedSnapshots.forEach(snapshot -> {
             // Create or update Repository entity with organization linking
             ensureRepositoryFromSnapshot(
                 workspace,
+                githubProvider,
                 snapshot.id(),
                 snapshot.nameWithOwner(),
                 snapshot.name(),
                 snapshot.isPrivate()
             );
             ensureRepositoryMonitorForInstallation(installationId, snapshot.nameWithOwner(), deferSync);
         });
-    private void ensureRepositoryFromSnapshot(
+    private void ensureRepositoryFromSnapshot(
         Workspace workspace,
+        GitProvider provider,
         long nativeId,
         String nameWithOwner,
         String name,
         boolean isPrivate
     ) {
         if (StringUtils.isBlank(nameWithOwner)) {
             return;
         }

-        GitProvider provider = gitProviderRepository
-            .findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
-            .orElseThrow(() ->
-                new IllegalStateException("GitProvider not found for type=GITHUB, serverUrl=https://github.com")
-            );
-
         repositoryRepository.upsertFromSnapshot(
             nativeId,
             provider.getId(),
             nameWithOwner,
             name,
             isPrivate,
             workspace.getOrganization() != null ? workspace.getOrganization().getId() : null
         );
     }
+
+    private GitProvider resolveGitHubProvider() {
+        return gitProviderRepository
+            .findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
+            .orElseThrow(() ->
+                new IllegalStateException("GitProvider not found for type=GITHUB, serverUrl=https://github.com")
+            );
+    }

Also applies to: 686-690

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryMonitorService.java`
around lines 468 - 476, The loop in WorkspaceRepositoryMonitorService that
iterates over allowedSnapshots calls a provider lookup on every iteration (seen
around ensureRepositoryFromSnapshot usage), causing repeated DB hits; move the
provider lookup(s) performed inside that snapshot-processing loop(s) out of the
loop into a local variable before iteration and reuse that cached
Provider/RepositoryProvider instance when calling ensureRepositoryFromSnapshot
(and the analogous loop around the other occurrence near the later block), so
the loop reuses the single fetched provider instead of querying the DB per
snapshot.
🧹 Nitpick comments (10)
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandler.java (1)

126-129: Provider lookup assumes pre-seeded database entry.

The IllegalStateException is appropriate for fail-fast behavior on misconfiguration. However, this creates an implicit runtime dependency on the GitHub provider being present in the database before any webhook can be processed.

Consider documenting this requirement or verifying the provider is seeded during application startup (e.g., via a @PostConstruct initializer or migration).

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandler.java`
around lines 126 - 129, The handler GitHubProjectStatusUpdateMessageHandler
calls gitProviderRepository.findByTypeAndServerUrl(...) and throws
IllegalStateException if the GitHub provider is missing, creating an implicit
runtime dependency; fix by ensuring the GitHub provider is seeded at startup
(add a seeding routine or migration) or explicitly document the requirement:
implement a startup initializer (e.g., in an application-scoped bean with
`@PostConstruct`) that checks for GitProviderType.GITHUB and "https://github.com"
using gitProviderRepository and creates the provider entry if absent, or add
migration scripts to guarantee the record exists before webhooks are processed;
keep ProcessingContext.forWebhook(...) usage unchanged.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandlerIntegrationTest.java (2)

35-36: Prefer constructor injection instead of new field injection.

The new GitProviderRepository dependency is added via field injection. Please keep this class aligned with constructor injection style.

♻️ Suggested refactor
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
@@
 `@DisplayName`("GitHub Installation Message Handler")
+@RequiredArgsConstructor(onConstructor_ = `@Autowired`)
 class GitHubInstallationMessageHandlerIntegrationTest extends BaseIntegrationTest {
@@
-    `@Autowired`
-    private GitHubInstallationMessageHandler handler;
+    private final GitHubInstallationMessageHandler handler;
@@
-    `@Autowired`
-    private ObjectMapper objectMapper;
+    private final ObjectMapper objectMapper;
@@
-    `@Autowired`
-    private GitProviderRepository gitProviderRepository;
+    private final GitProviderRepository gitProviderRepository;

As per coding guidelines, "Use constructor injection via @RequiredArgsConstructor annotation".

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandlerIntegrationTest.java`
around lines 35 - 36, The test class
GitHubInstallationMessageHandlerIntegrationTest currently uses field injection
for GitProviderRepository; change it to constructor injection by making the
gitProviderRepository field final and removing the `@Autowired` field injection,
then add a constructor (or annotate the class with Lombok's
`@RequiredArgsConstructor`) so the repository is injected via the class
constructor; update any imports and remove the unused `@Autowired` to align with
constructor-injection style.

41-44: Make provider bootstrap resilient to parallel execution.

This find-then-save pattern is not atomic and can intermittently fail with duplicate-key errors if tests run concurrently. Prefer an atomic upsert path (or duplicate-safe retry).

🛠️ Duplicate-safe alternative
+import org.springframework.dao.DataIntegrityViolationException;
@@
         gitProviderRepository
             .findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
-            .orElseGet(() -> gitProviderRepository.save(new GitProvider(GitProviderType.GITHUB, "https://github.com")));
+            .orElseGet(() -> {
+                try {
+                    return gitProviderRepository.saveAndFlush(
+                        new GitProvider(GitProviderType.GITHUB, "https://github.com")
+                    );
+                } catch (DataIntegrityViolationException ex) {
+                    return gitProviderRepository
+                        .findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
+                        .orElseThrow();
+                }
+            });

As per coding guidelines, "Test cases may run in parallel, so avoid required cleanup steps and assume that there might be data from previous tests in the database".

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandlerIntegrationTest.java`
around lines 41 - 44, The current non-atomic find-then-save using
gitProviderRepository.findByTypeAndServerUrl(...).orElseGet(() ->
gitProviderRepository.save(new GitProvider(...))) can race and cause
duplicate-key errors when tests run in parallel; change to an
atomic/duplicate-safe upsert or a duplicate-safe retry: attempt to save the new
GitProvider via gitProviderRepository.save(...) inside a try/catch that catches
the DB duplicate-key/DataIntegrityViolationException and, on that exception,
re-query using gitProviderRepository.findByTypeAndServerUrl(...) to return the
existing entity, or replace with a repository-level upsert method if available;
reference gitProviderRepository, findByTypeAndServerUrl, save and GitProvider to
locate where to implement this handling.
server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryCoverageIntegrationTest.java (1)

50-53: Consider using upsert for parallel-safe test setup.

The find-or-create pattern could cause unique constraint violations if tests run in parallel—both might find nothing after cleanDatabase() and attempt to insert simultaneously. Per the PR objectives, native upsert patterns are preferred to prevent race conditions.

Consider extracting this to a test fixture utility that uses saveAndFlush with conflict handling, or verify that these tests are configured to run serially.

♻️ Potential approach using test fixture helper
// In a shared test utility class:
public static GitProvider ensureGitHubProvider(GitProviderRepository repo) {
    return repo.findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
        .orElseGet(() -> {
            try {
                return repo.saveAndFlush(new GitProvider(GitProviderType.GITHUB, "https://github.com"));
            } catch (DataIntegrityViolationException e) {
                // Another test created it concurrently
                return repo.findByTypeAndServerUrl(GitProviderType.GITHUB, "https://github.com")
                    .orElseThrow();
            }
        });
}

Or if GitProviderRepository has an upsert method (as mentioned in PR objectives), use that directly.

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

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryCoverageIntegrationTest.java`
around lines 50 - 53, Replace the current find-or-create block using
gitProviderRepository.findByTypeAndServerUrl(...).orElseGet(...) with a
parallel-safe upsert helper: extract a shared test utility method (e.g.,
ensureGitHubProvider(GitProviderRepository)) that tries saveAndFlush(new
GitProvider(GitProviderType.GITHUB, "https://github.com")) and on
DataIntegrityViolationException retries a findByTypeAndServerUrl(...) to return
the existing row; alternatively call an existing repository upsert method if
available; update the test to call this helper instead of the inline
find-or-create to avoid race conditions when tests run in parallel.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandler.java (2)

121-135: LGTM — consider a minor readability improvement.

The logic correctly resolves the item's synthetic PK via nodeId instead of getDatabaseId() (which returns the native ID). The nested ifPresent calls work correctly, though they could be flattened for readability.

♻️ Optional: Flatten nested Optional handling
 case DELETED -> {
     // Resolve the item's synthetic PK via nodeId since getDatabaseId() returns the native ID.
     // Delete events include project_node_id, so we can resolve the project first.
     String itemNodeId = itemDto.nodeId();
     String projNodeId = itemDto.projectNodeId();
     if (itemNodeId != null && projNodeId != null) {
-        projectRepository
-            .findByNodeId(projNodeId)
-            .ifPresent(project ->
-                projectItemRepository
-                    .findByProjectIdAndNodeId(project.getId(), itemNodeId)
-                    .ifPresent(item -> itemProcessor.delete(item.getId(), project.getId(), context))
-            );
+        projectRepository.findByNodeId(projNodeId)
+            .flatMap(project -> projectItemRepository
+                .findByProjectIdAndNodeId(project.getId(), itemNodeId)
+                .map(item -> Map.entry(item.getId(), project.getId())))
+            .ifPresent(entry -> itemProcessor.delete(entry.getKey(), entry.getValue(), context));
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandler.java`
around lines 121 - 135, Flatten the nested Optional handling in
GitHubProjectItemMessageHandler: instead of calling
projectRepository.findByNodeId(projNodeId).ifPresent(...projectItemRepository.findByProjectIdAndNodeId(...).ifPresent(...))
extract the project via projectRepository.findByNodeId(projNodeId).map(project
-> project.getId()) or use flatMap to chain to
projectItemRepository.findByProjectIdAndNodeId(projectId, itemNodeId) and then
ifPresent to call itemProcessor.delete(item.getId(), projectId, context); this
reduces nesting while keeping the same null checks on itemDto.nodeId() and
itemDto.projectNodeId() and preserves use of projectRepository,
projectItemRepository, itemProcessor, and itemDto.

107-110: Extract the hardcoded GitHub URL to a constant for consistency and maintainability.

The literal string "https://github.com" is used in multiple places throughout the codebase when looking up the GitHub provider. Consider extracting this to a shared constant (e.g., GITHUB_SERVER_URL or GITHUB_COM_SERVER_URL) to simplify future updates and improve consistency.

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandler.java`
around lines 107 - 110, Replace the hardcoded "https://github.com" literal used
in GitHubProjectItemMessageHandler (in the call to
gitProviderRepository.findByTypeAndServerUrl and any other occurrences) with a
shared constant like GITHUB_COM_SERVER_URL (e.g., public static final String
GITHUB_COM_SERVER_URL = "https://github.com") placed in a common location (a
constants class or the GitHubProjectItemMessageHandler if there is no shared
constants class), and update the call to use that constant so all lookups use
the centralized GITHUB_COM_SERVER_URL symbol for consistency and
maintainability.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java (2)

39-41: Consider using @Slf4j annotation for consistency.

The coding guidelines recommend using Lombok's @Slf4j annotation instead of manual logger creation with LoggerFactory. This improves consistency across the codebase.

♻️ Suggested refactor
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
 public abstract class BaseGitLabProcessor {
 
-    private static final Logger log = LoggerFactory.getLogger(BaseGitLabProcessor.class);

The Logger and LoggerFactory imports can also be removed after this change.

As per coding guidelines: "Use Lombok annotations: @RequiredArgsConstructor, @Slf4j".

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`
around lines 39 - 41, Replace the manual SLF4J logger in BaseGitLabProcessor
with Lombok's `@Slf4j`: remove the private static final Logger log and the
LoggerFactory import, add the `@Slf4j` annotation to the BaseGitLabProcessor
class, and update any usages of the log variable to rely on the generated slf4j
logger; also remove the explicit Logger import since Lombok provides the logger.

59-80: Consider using @RequiredArgsConstructor for constructor injection.

The coding guidelines recommend using Lombok's @RequiredArgsConstructor annotation instead of manually writing constructors. This reduces boilerplate and ensures consistency.

♻️ Suggested refactor
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
 public abstract class BaseGitLabProcessor {
 
     // ... (remove manual constructor, keep fields as-is)
-
-    protected BaseGitLabProcessor(
-        UserRepository userRepository,
-        LabelRepository labelRepository,
-        RepositoryRepository repositoryRepository,
-        ScopeIdResolver scopeIdResolver,
-        RepositoryScopeFilter repositoryScopeFilter,
-        GitLabProperties gitLabProperties
-    ) {
-        this.userRepository = userRepository;
-        this.labelRepository = labelRepository;
-        this.repositoryRepository = repositoryRepository;
-        this.scopeIdResolver = scopeIdResolver;
-        this.repositoryScopeFilter = repositoryScopeFilter;
-        this.gitLabProperties = gitLabProperties;
-    }

As per coding guidelines: "Use constructor injection via @RequiredArgsConstructor annotation".

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`
around lines 59 - 80, The BaseGitLabProcessor class currently defines a manual
constructor for final fields userRepository, labelRepository,
repositoryRepository, scopeIdResolver, repositoryScopeFilter and
gitLabProperties; replace this boilerplate by annotating the class with Lombok's
`@RequiredArgsConstructor` (import lombok.RequiredArgsConstructor) and remove the
explicit constructor method to rely on Lombok-generated constructor; ensure the
fields remain final and Lombok is available on the project classpath.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandler.java (1)

124-131: Consider adding debug logging when project is not found during delete.

When a DELETED event arrives for a project that doesn't exist locally (e.g., never synced or already deleted), the current code silently does nothing. Adding a debug log would improve observability for troubleshooting webhook processing issues.

🔍 Proposed enhancement
             case DELETED -> {
                 // Resolve the synthetic PK via nodeId since getDatabaseId() returns the native ID
                 String nodeId = projectDto.nodeId();
                 if (nodeId != null) {
                     projectRepository
                         .findByNodeId(nodeId)
-                        .ifPresent(project -> projectProcessor.delete(project.getId(), context));
+                        .ifPresentOrElse(
+                            project -> projectProcessor.delete(project.getId(), context),
+                            () -> log.debug("Skipped project delete: reason=projectNotFound, nodeId={}", nodeId)
+                        );
+                } else {
+                    log.debug("Skipped project delete: reason=missingNodeId");
                 }
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandler.java`
around lines 124 - 131, In GitHubProjectMessageHandler's DELETED branch, add a
debug log when projectRepository.findByNodeId(projectDto.nodeId()) returns empty
so failed/no-op deletes are observable; after retrieving nodeId from
projectDto.nodeId(), if nodeId is null log a debug stating nodeId missing,
otherwise call projectRepository.findByNodeId(nodeId).ifPresentOrElse(...) where
the present path calls projectProcessor.delete(project.getId(), context) and the
empty path emits a debug-level message including the nodeId and relevant context
to aid troubleshooting.
server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadProcessor.java (1)

23-34: Consider using Lombok annotations for consistency.

The class uses an explicit constructor and manual Logger creation. For consistency with project conventions, consider using @RequiredArgsConstructor and @Slf4j.

♻️ Proposed refactor
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;

+@Slf4j
+@RequiredArgsConstructor
 `@Service`
 public class GitHubPullRequestReviewThreadProcessor {

-    private static final Logger log = LoggerFactory.getLogger(GitHubPullRequestReviewThreadProcessor.class);
-
     private final PullRequestReviewThreadRepository threadRepository;
     private final ApplicationEventPublisher eventPublisher;
-
-    public GitHubPullRequestReviewThreadProcessor(
-        PullRequestReviewThreadRepository threadRepository,
-        ApplicationEventPublisher eventPublisher
-    ) {
-        this.threadRepository = threadRepository;
-        this.eventPublisher = eventPublisher;
-    }

As per coding guidelines: "Use constructor injection via @RequiredArgsConstructor annotation" and "Use Lombok annotations: @RequiredArgsConstructor, @Slf4j".

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

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadProcessor.java`
around lines 23 - 34, Replace the manual Logger and explicit constructor in
GitHubPullRequestReviewThreadProcessor with Lombok annotations: add
`@RequiredArgsConstructor` to generate the constructor for the final fields
threadRepository and eventPublisher and add `@Slf4j` to provide the logger; remove
the private static final Logger log field and the explicit constructor method,
keep the final fields (PullRequestReviewThreadRepository threadRepository,
ApplicationEventPublisher eventPublisher) as-is, and add the necessary imports
for lombok.RequiredArgsConstructor and lombok.extern.slf4j.Slf4j.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/contributor/erd/schema.mmd`:
- Around line 132-133: The ERD generator is emitting duplicate column lines for
entities like DiscussionCategory (duplicate `VARCHAR(128) slug UK "NOT NULL"`)
and duplicate `BIGINT repository_id FK,UK` entries; fix the generator that
builds the columns list (the function that composes or maps entity.fields into
output) to deduplicate entries by the field identifier (e.g., field name +
constraint set) before rendering, ensure the logic that appends unique
keys/foreign keys (used for Discussion and DiscussionCategory output) only emits
each constraint once, and regenerate the schema so each column and constraint
appears only a single time.
- Around line 105-106: The Discussion entity generator is emitting duplicate
field lines (e.g., the `number` field and `repository_id` FK/UK), so update the
schema generation logic (the function that builds Discussion fields—look for
methods like buildEntityFields, generateDiscussionSchema, or similar in the ERD
generator) to deduplicate fields before rendering: collect fields into a map/set
keyed by a stable identity (field name + type + modifiers like FK/UK/NOT NULL)
and emit only unique entries, then regenerate the docs with `npm run
db:generate-erd-docs` rather than hand-editing docs/contributor/erd/schema.mmd.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`:
- Around line 227-230: The deterministic label ID generation in
generateDeterministicLabelId currently uses String.hashCode(), which risks
collisions; replace that with a stronger 32-bit hash (e.g., use a truncated
SHA-256 or MurmurHash3 32-bit) over labelName to improve distribution, then
combine the 32-bit hash with repositoryId the same way ((repositoryId << 32) |
(hash & 0xFFFFFFFFL)) and return the negated long as before; keep existing
callers/behavior (insertIfAbsent and findByRepositoryIdAndName) unchanged so the
fallback logic remains intact.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java`:
- Around line 86-98: The IssueCreated events are losing webhook metadata because
process(...) calls upsertIssue(...) with a synchronized context (via
context.forSync()), so change the call site in GitLabIssueProcessor.process(...)
to use the original webhook context (or a context variant that preserves
source/correlation/scope/action) instead of context.forSync(); ensure the
upsertIssue(...) invocation (the call that currently passes context.repository()
/ context.forSync()) forwards the unmodified context used for webhook events so
created events retain webhook metadata.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/RepositoryRepository.java`:
- Around line 98-101: The upsert in RepositoryRepository.java's SQL conflict
branch updates is_private but not visibility, risking inconsistency; modify the
DO UPDATE SET clause in the ON CONFLICT (provider_id, name_with_owner) branch to
also set visibility = EXCLUDED.visibility (alongside the existing name and
is_private) so both columns remain synchronized on conflicts while keeping the
existing COALESCE for organization_id.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java`:
- Around line 69-71: Replace the non-atomic find-then-save for GitProvider by
making the create operation idempotent and race-safe: when resolving gitProvider
via gitProviderRepository and GitProviderType.GITHUB / "https://github.com",
attempt to save if missing but catch the unique
constraint/DataIntegrityViolationException and then re-query the repository;
i.e., keep the lookup using gitProviderRepository.findByTypeAndServerUrl(...)
but if absent, call gitProviderRepository.save(new GitProvider(...)) inside a
try/catch that on constraint violation performs a second findByTypeAndServerUrl
to return the existing entity—use the existing gitProvider variable and
gitProviderRepository symbols to locate the code.

In `@server/intelligence-service/src/shared/db/schema.ts`:
- Around line 441-443: The schema contains duplicate unique constraints (e.g.,
unique("uq_discussion_repo_number") and unique("uknlcwyn2relkgw95s8okgpkqrt")
both on discussion.number and discussion.repositoryId, and a similar duplicate
on discussion_category referenced in the diff); remove the duplicate constraint
at the migration level (use Liquibase to drop the redundant constraint names
such as uknlcwyn2relkgw95s8okgpkqrt and uk6cjmvjyh5jc9bfnn8i9wggbo5 from their
respective tables) then regenerate the generated artifact
server/intelligence-service/src/shared/db/schema.ts using the project’s schema
generation command so only the canonical unique constraints remain.

---

Duplicate comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryMonitorService.java`:
- Around line 468-476: The loop in WorkspaceRepositoryMonitorService that
iterates over allowedSnapshots calls a provider lookup on every iteration (seen
around ensureRepositoryFromSnapshot usage), causing repeated DB hits; move the
provider lookup(s) performed inside that snapshot-processing loop(s) out of the
loop into a local variable before iteration and reuse that cached
Provider/RepositoryProvider instance when calling ensureRepositoryFromSnapshot
(and the analogous loop around the other occurrence near the later block), so
the loop reuses the single fetched provider instead of querying the DB per
snapshot.

---

Nitpick comments:
In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java`:
- Around line 39-41: Replace the manual SLF4J logger in BaseGitLabProcessor with
Lombok's `@Slf4j`: remove the private static final Logger log and the
LoggerFactory import, add the `@Slf4j` annotation to the BaseGitLabProcessor
class, and update any usages of the log variable to rely on the generated slf4j
logger; also remove the explicit Logger import since Lombok provides the logger.
- Around line 59-80: The BaseGitLabProcessor class currently defines a manual
constructor for final fields userRepository, labelRepository,
repositoryRepository, scopeIdResolver, repositoryScopeFilter and
gitLabProperties; replace this boilerplate by annotating the class with Lombok's
`@RequiredArgsConstructor` (import lombok.RequiredArgsConstructor) and remove the
explicit constructor method to rely on Lombok-generated constructor; ensure the
fields remain final and Lombok is available on the project classpath.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandler.java`:
- Around line 121-135: Flatten the nested Optional handling in
GitHubProjectItemMessageHandler: instead of calling
projectRepository.findByNodeId(projNodeId).ifPresent(...projectItemRepository.findByProjectIdAndNodeId(...).ifPresent(...))
extract the project via projectRepository.findByNodeId(projNodeId).map(project
-> project.getId()) or use flatMap to chain to
projectItemRepository.findByProjectIdAndNodeId(projectId, itemNodeId) and then
ifPresent to call itemProcessor.delete(item.getId(), projectId, context); this
reduces nesting while keeping the same null checks on itemDto.nodeId() and
itemDto.projectNodeId() and preserves use of projectRepository,
projectItemRepository, itemProcessor, and itemDto.
- Around line 107-110: Replace the hardcoded "https://github.com" literal used
in GitHubProjectItemMessageHandler (in the call to
gitProviderRepository.findByTypeAndServerUrl and any other occurrences) with a
shared constant like GITHUB_COM_SERVER_URL (e.g., public static final String
GITHUB_COM_SERVER_URL = "https://github.com") placed in a common location (a
constants class or the GitHubProjectItemMessageHandler if there is no shared
constants class), and update the call to use that constant so all lookups use
the centralized GITHUB_COM_SERVER_URL symbol for consistency and
maintainability.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandler.java`:
- Around line 124-131: In GitHubProjectMessageHandler's DELETED branch, add a
debug log when projectRepository.findByNodeId(projectDto.nodeId()) returns empty
so failed/no-op deletes are observable; after retrieving nodeId from
projectDto.nodeId(), if nodeId is null log a debug stating nodeId missing,
otherwise call projectRepository.findByNodeId(nodeId).ifPresentOrElse(...) where
the present path calls projectProcessor.delete(project.getId(), context) and the
empty path emits a debug-level message including the nodeId and relevant context
to aid troubleshooting.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandler.java`:
- Around line 126-129: The handler GitHubProjectStatusUpdateMessageHandler calls
gitProviderRepository.findByTypeAndServerUrl(...) and throws
IllegalStateException if the GitHub provider is missing, creating an implicit
runtime dependency; fix by ensuring the GitHub provider is seeded at startup
(add a seeding routine or migration) or explicitly document the requirement:
implement a startup initializer (e.g., in an application-scoped bean with
`@PostConstruct`) that checks for GitProviderType.GITHUB and "https://github.com"
using gitProviderRepository and creates the provider entry if absent, or add
migration scripts to guarantee the record exists before webhooks are processed;
keep ProcessingContext.forWebhook(...) usage unchanged.

In
`@server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadProcessor.java`:
- Around line 23-34: Replace the manual Logger and explicit constructor in
GitHubPullRequestReviewThreadProcessor with Lombok annotations: add
`@RequiredArgsConstructor` to generate the constructor for the final fields
threadRepository and eventPublisher and add `@Slf4j` to provide the logger; remove
the private static final Logger log field and the explicit constructor method,
keep the final fields (PullRequestReviewThreadRepository threadRepository,
ApplicationEventPublisher eventPublisher) as-is, and add the necessary imports
for lombok.RequiredArgsConstructor and lombok.extern.slf4j.Slf4j.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandlerIntegrationTest.java`:
- Around line 35-36: The test class
GitHubInstallationMessageHandlerIntegrationTest currently uses field injection
for GitProviderRepository; change it to constructor injection by making the
gitProviderRepository field final and removing the `@Autowired` field injection,
then add a constructor (or annotate the class with Lombok's
`@RequiredArgsConstructor`) so the repository is injected via the class
constructor; update any imports and remove the unused `@Autowired` to align with
constructor-injection style.
- Around line 41-44: The current non-atomic find-then-save using
gitProviderRepository.findByTypeAndServerUrl(...).orElseGet(() ->
gitProviderRepository.save(new GitProvider(...))) can race and cause
duplicate-key errors when tests run in parallel; change to an
atomic/duplicate-safe upsert or a duplicate-safe retry: attempt to save the new
GitProvider via gitProviderRepository.save(...) inside a try/catch that catches
the DB duplicate-key/DataIntegrityViolationException and, on that exception,
re-query using gitProviderRepository.findByTypeAndServerUrl(...) to return the
existing entity, or replace with a repository-level upsert method if available;
reference gitProviderRepository, findByTypeAndServerUrl, save and GitProvider to
locate where to implement this handling.

In
`@server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryCoverageIntegrationTest.java`:
- Around line 50-53: Replace the current find-or-create block using
gitProviderRepository.findByTypeAndServerUrl(...).orElseGet(...) with a
parallel-safe upsert helper: extract a shared test utility method (e.g.,
ensureGitHubProvider(GitProviderRepository)) that tries saveAndFlush(new
GitProvider(GitProviderType.GITHUB, "https://github.com")) and on
DataIntegrityViolationException retries a findByTypeAndServerUrl(...) to return
the existing row; alternatively call an existing repository upsert method if
available; update the test to call this helper instead of the inline
find-or-create to avoid race conditions when tests run in parallel.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ae64ac and 6aff117.

📒 Files selected for processing (23)
  • docs/contributor/erd/schema.mmd
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/ProcessingContext.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/common/gitlab/BaseGitLabProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueSyncService.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectItemMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewthread/github/GitHubPullRequestReviewThreadProcessor.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/repository/RepositoryRepository.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandler.java
  • server/application-server/src/main/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryMonitorService.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/installation/github/GitHubInstallationMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/issue/gitlab/GitLabIssueMessageHandlerTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectStatusUpdateMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequest/github/GitHubPullRequestMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/pullrequestreviewcomment/github/GitHubPullRequestReviewCommentMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/repository/github/GitHubMemberMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/team/github/GitHubTeamMessageHandlerIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/WorkspaceRepositoryCoverageIntegrationTest.java
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/workspace/settings/WorkspaceTeamSettingsControllerIntegrationTest.java
  • server/intelligence-service/src/shared/db/schema.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/application-server/src/test/java/de/tum/in/www1/hephaestus/gitprovider/project/github/GitHubProjectMessageHandlerIntegrationTest.java

Comment thread docs/contributor/erd/schema.mmd Outdated
Comment thread docs/contributor/erd/schema.mmd Outdated
Comment thread server/intelligence-service/src/shared/db/schema.ts
The user table now requires native_id and provider_id (NOT NULL FK to
git_provider). Update createTestFixtures to insert a git_provider row
and include these fields when creating test users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FelixTJDietrich and others added 18 commits March 1, 2026 00:34
The authorization test creates documents referencing the test user. The
cleanup must delete documents before the user to satisfy FK constraints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove duplicate Hibernate-generated unique constraints that were
cleaned up by migrations but persisted in the local database.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nthetic PK migration

- Fix BaseGitHubProcessor.findOrCreateMilestone to use natural key
  (number + repositoryId) instead of findById with native ID
- Fix GitHubPullRequestProcessorIntegrationTest to use getNativeId()
  and synthetic PK for repository FK references

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace all findById(nativeId) calls with natural key lookups
(findByRepositoryIdAndNumber, findByNativeIdAndProviderId) and
change getId() assertions to getNativeId() throughout the test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…for synthetic PKs

- Fix Issue/Discussion processDeleted to use natural key lookup instead
  of deleteById(nativeId) which silently does nothing with synthetic PKs
- Fix remaining integration tests: Issue, Discussion, DiscussionComment,
  GitLab Issue handler tests for synthetic PK lookups and lazy init

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tation

- Fix provider-scoped lookups: GitHubDataSyncService, GitHubRepositorySyncService,
  GitLabPushMessageHandler, WorkspaceProvisioningService, TestUserFactory
- Fix webhook author vs actor bug in GitLabIssueProcessor (resolveWebhookAuthor)
- Fix GitProvider equals/hashCode for JPA null-id safety
- Add null providerId guards in GitHubOrganizationProcessor
- Add OrganizationRepository.findByLoginIgnoreCaseAndProviderId
- Add visibility sync in RepositoryRepository.upsertFromSnapshot
- Optimize repeated provider lookups in WorkspaceRepositoryMonitorService
- Add sanitizeForLog for user identifiers in log statements
- Remove all stale negated-ID documentation references
- Clean up noisy inline comments in GitHubRepositorySyncService
- Fix ProcessingContext Javadoc (GitHub → git provider) and remove self-import
- Add missing Javadoc on BaseGitLabProcessor, WorkspaceProvisioningService methods

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Includes provider-scoped UserRepository methods, GitHubTeamSyncService
provider resolution, GraphQL fragments, ERD schema, commit author
resolver changes, and corresponding test updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lombok equals/hashCode:
- Replace Lombok @EqualsAndHashCode on BaseGitServiceEntity with manual
  JPA-safe implementation (returns false when id is null)
- Remove @EqualsAndHashCode(callSuper=true) from Project, ProjectItem,
  ProjectStatusUpdate (lazy relationship traversal + LazyInitException)
- Replace Lombok @EqualsAndHashCode on Commit with manual implementation

Migration safety:
- Drop existing DEFAULT before adding IDENTITY (fixes team table which
  uses serial from autoIncrement, causing "already has a default" error)
- Add (provider_id, native_id) unique constraints for all 12 remaining
  entity tables (was only on user and organization)
- Make seed data idempotent with ON CONFLICT DO NOTHING

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tities and regenerate DB docs

Add @UniqueConstraint(provider_id, native_id) annotations to all 12 entity
classes that were missing them (Repository, Issue, Milestone, IssueComment,
PullRequestReviewComment, PullRequestReviewThread, Discussion,
DiscussionComment, Team, Project, ProjectItem, ProjectStatusUpdate).
This aligns the JPA model with the migration changeset 1772284124265-12
and eliminates schema drift. Regenerate ERD and Drizzle schema docs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hCode

Apply Vlad Mihalcea / Baeldung JPA best practices:

- Add fetch=FetchType.LAZY to 19 @ManyToOne/@OnetoOne annotations that
  defaulted to EAGER, preventing cascading JOINs on every entity load
- Fix PullRequestBadPractice.hashCode() from Objects.hashCode(id) to
  getClass().hashCode() (Vlad Mihalcea pattern for generated IDs)
- Add missing equals/hashCode to PullRequestReview, BadPracticeDetection,
  BadPracticeFeedback using the JPA-safe id-null-guard pattern
- Expand wildcard jakarta.persistence.* imports to explicit imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add JOIN FETCH for Label.repository in WorkspaceTeamLabelFilterRepository
queries to prevent LazyInitializationException when LabelInfoDTO.fromLabel()
accesses the repository outside a transaction. Wrap label repository
assertion in TransactionTemplate in GitHubLabelMessageHandlerIntegrationTest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ssignees

- Add follow-up pagination for labels and assignees when initial page overflows
- Add count fields to GraphQL queries for post-sync overflow detection
- Add partial GraphQL error handling across all sync services
- Increase issue sync page size from 20 to 30 for better throughput
- Add @tag annotations to all GitLab unit tests for proper CI classification
- Add postgres-data*.tgz to .gitignore
ExperiencePointCalculator.calculateIssueCommentExperiencePoints() threw
ClassCastException when Issue was a HibernateProxy (SINGLE_TABLE
inheritance + lazy load). Use Hibernate.unproxy() and instanceof
pattern matching instead of isPullRequest() + unsafe cast.
…raint drops

- Drop fk_workspace_organization before TRUNCATE organization CASCADE to
  prevent structural cascade from wiping workspace data (PostgreSQL
  TRUNCATE CASCADE follows FK definitions regardless of row references)
- Drop uk_user_login constraint (added by migration 1) in migration 2
  before adding provider-scoped replacement
- Drop uk_team_organization_name and replace with provider-scoped
  uk_team_provider_organization_name (provider_id, organization, name)
- Update Team.java entity annotation to match migration constraint name
…ngelog

Merge content of 1772284124265_changelog.xml into 1768089179000_changelog.xml
as Section 45 (Phases 1-8/12). Inline native_id, provider_id, IDENTITY, FK,
and unique constraints into CREATE TABLE for 5 later-created tables (project,
project_item, project_status_update, discussion, discussion_comment). Fix XML
parse error in preConditions ordering for case-insensitive user login changeset.

Tested: fresh staging data restore, Liquibase migration, deep audit of all 14
BaseGitServiceEntity tables (native_id, provider_id, IDENTITY, FK, unique
constraints), 1026/1026 unit tests pass, npm run check passes.
CI generates schema from a fresh Liquibase migration which produces clean
named constraints. Our local staging DB had legacy unnamed constraints
alongside the new named ones, causing duplicates in drizzle-kit output.
Revert existing changelog files (1768, 1771, 1772037) to their main
state and move all branch migration changes into a single new file
(1772284124265_changelog.xml) that runs last via master.xml.

The new changelog handles all 14 BaseGitServiceEntity tables:
- Creates git_provider table with GITHUB/GITLAB seed data
- Adds native_id and provider_id columns to all 14 tables
- Converts id columns to IDENTITY (GENERATED BY DEFAULT)
- Adds FK constraints to git_provider and provider-scoped unique constraints
- Fixes TRUNCATE CASCADE safety in 1768 (drop/re-add workspace FK)

Regenerates Drizzle schema and ERD docs to reflect new column ordering.
@FelixTJDietrich FelixTJDietrich merged commit ee51df1 into main Mar 2, 2026
46 checks passed
@FelixTJDietrich FelixTJDietrich deleted the feat/gitprovider-gitlab-sync-issue branch March 2, 2026 08:48
@FelixTJDietrich
Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 0.20.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@FelixTJDietrich FelixTJDietrich added the released Included in a published release label Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

application-server Spring Boot server: APIs, business logic, database dependencies Package updates, version bumps, lock file changes documentation Improvements or additions to documentation feature New feature or enhancement intelligence-service TypeScript AI service: LLM orchestration, mentor chat released Included in a published release size:XXL This PR changes 1000+ lines, ignoring generated files. webhook-ingest TypeScript webhook receiver: GitHub/GitLab event intake

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(gitprovider): GitLab sync - Issue entity

2 participants