Skip to content

1.8.1 rc - #248

Merged
rostilos merged 11 commits into
mainfrom
1.8.1-rc
Aug 12, 2026
Merged

1.8.1 rc#248
rostilos merged 11 commits into
mainfrom
1.8.1-rc

Conversation

@rostilos

@rostilos rostilos commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Added secure public sharing and preview links for QA documentation.
    • QA previews now include overview, environment notes, and structured test cases while excluding internal details.
    • QA documents can be reused during handoff retries, reducing unnecessary regeneration.
    • Improved QA generation with standardized, machine-detectable test-case sections and repair support.
    • RAG indexing now coordinates jobs more reliably and prevents stale updates.
    • Jira comments now render plain URLs as clickable links.
  • Bug Fixes

    • Improved recovery of interrupted indexing operations and lock handling.
    • Prevented malformed public-share requests and expired links from exposing content.

rostilos and others added 7 commits August 11, 2026 17:37
- add reusable public-share links backed by opaque hashed credentials
- extract structured test cases from generated QA documentation
- repair generated documents that omit marked test scenarios
- preserve full QA documents while sharing test-case preview links in Jira
- render public preview URLs as Jira links and keep failed handoffs retryable
- reconcile interrupted RAG jobs, project states, and revision locks
- isolate recovery scheduling from optional alias reconciliation
- preserve all Jira QA sections while replacing only test scenarios with a share link
- expose sanitized QA content after public-token validation
- redirect authorized users to the project QA document
- split environment notes into a separate response section
- prevent N/A QA guide titles
- make stale lock and RAG recovery concurrency-safe
- reuse persisted QA documents when preview or Jira handoff fails
- validate marked test cases and trim Jira URL delimiters correctly
- align public QA share contracts and include the frontend preview route
- prevent stale RAG jobs from overwriting newer indexing status
- persist exact lock ownership for safe recovery and parallel branch indexing
- regenerate QA documentation when a pending handoff belongs to another task
- add migration and regression coverage
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Too many files!

This PR contains 139 files, which is 39 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 78bb11a6-6c02-425c-8fbc-d3cda79693dc

📥 Commits

Reviewing files that changed from the base of the PR and between a84e9e6 and 9296e6d.

📒 Files selected for processing (139)
  • deployment/config/inference-orchestrator/.env.sample
  • deployment/config/java-shared/application.properties.sample
  • deployment/config/rag-pipeline/.env.sample
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/aiclient/AiAnalysisClient.java
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/aiclient/AiCommandClient.java
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessor.java
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/PullRequestAnalysisProcessor.java
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/service/AnalysisLockService.java
  • java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/service/branch/BranchAnalysisGateService.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/aiclient/AiAnalysisClientTest.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/aiclient/AiCommandClientTest.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessorTest.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/processor/analysis/PullRequestAnalysisProcessorTest.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/service/AnalysisLockServiceTest.java
  • java-ecosystem/libs/analysis-engine/src/test/java/org/rostilos/codecrow/analysisengine/service/branch/BranchAnalysisGateServiceTest.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/rag/RagBranchIndex.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/analysis/AnalysisLockRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/analysis/RagIndexStatusRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/job/JobLogRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/job/JobRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/rag/RagBranchIndexGenerationRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/rag/RagBranchIndexRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/rag/RagIndexOperationRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/AnalysisJobService.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/JobService.java
  • java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.29.0__rag_transient_cleanup_claim.sql
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/persistence/repository/job/JobRepositoryRecoveryQueryTest.java
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/persistence/repository/job/LegacyRagJobRecoveryQueryTest.java
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/persistence/repository/rag/RagIndexOperationRecoveryQueryTest.java
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/AnalysisJobServiceTest.java
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/JobServiceLegacyRagRecoveryTest.java
  • java-ecosystem/libs/events/src/main/java/org/rostilos/codecrow/events/EventNotificationEmitter.java
  • java-ecosystem/libs/events/src/test/java/org/rostilos/codecrow/events/EventNotificationEmitterTest.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/BranchIndexBuildAdmissionService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/BranchIndexGenerationBuildService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/BranchIndexMaintenanceService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/LegacyRagJobLeaseService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/LegacyRagJobRecoveryService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/LegacyRagUpdateCompletionService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationHeartbeatService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagTransientBranchIndexCleanupService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/client/RagPipelineClient.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImpl.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/BranchIndexBuildAdmissionServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/BranchIndexGenerationBuildServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/BranchIndexMaintenanceServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/LegacyRagJobLeaseServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/LegacyRagJobRecoveryServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/LegacyRagUpdateCompletionServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationHeartbeatServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagTransientBranchIndexCleanupServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/client/RagPipelineClientTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImplTest.java
  • java-ecosystem/services/pipeline-agent/src/it/java/org/rostilos/codecrow/pipelineagent/BranchResolverFlowIT.java
  • java-ecosystem/services/pipeline-agent/src/it/java/org/rostilos/codecrow/pipelineagent/LineTrackingFlowIT.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/webhookhandler/BitbucketCloudBranchWebhookHandler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/webhookhandler/BitbucketCloudPullRequestWebhookHandler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/BranchHealthScheduler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/PipelineJobService.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookJobRecoveryScheduler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/github/webhookhandler/GitHubPullRequestWebhookHandler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/webhookhandler/GitLabMergeRequestWebhookHandler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/webhookhandler/GitLabMrMergeWebhookHandler.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocGenerationContext.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocGenerationService.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessorTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/generic/service/BranchHealthSchedulerTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookJobRecoverySchedulerTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListenerTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocGenerationServiceTest.java
  • python-ecosystem/inference-orchestrator/integration/test_rag_client_extended.py
  • python-ecosystem/inference-orchestrator/src/api/app.py
  • python-ecosystem/inference-orchestrator/src/api/routers/commands.py
  • python-ecosystem/inference-orchestrator/src/api/routers/qa_documentation.py
  • python-ecosystem/inference-orchestrator/src/api/routers/review.py
  • python-ecosystem/inference-orchestrator/src/server/command_queue_consumer.py
  • python-ecosystem/inference-orchestrator/src/server/queue_consumer.py
  • python-ecosystem/inference-orchestrator/src/service/command/command_service.py
  • python-ecosystem/inference-orchestrator/src/service/qa_documentation/base_orchestrator.py
  • python-ecosystem/inference-orchestrator/src/service/qa_documentation/qa_doc_orchestrator.py
  • python-ecosystem/inference-orchestrator/src/service/qa_documentation/qa_doc_service.py
  • python-ecosystem/inference-orchestrator/src/service/rag/rag_client.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/orchestrator.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/stage_0_planning.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/stage_1_file_review.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/stage_2_cross_file.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/stage_3_aggregation.py
  • python-ecosystem/inference-orchestrator/src/service/review/orchestrator/verification_agent.py
  • python-ecosystem/inference-orchestrator/src/service/review/review_service.py
  • python-ecosystem/inference-orchestrator/tests/prompt_dry_run_neutral_fixture.py
  • python-ecosystem/inference-orchestrator/tests/test_command_queue_consumer.py
  • python-ecosystem/inference-orchestrator/tests/test_command_service.py
  • python-ecosystem/inference-orchestrator/tests/test_llm_factory.py
  • python-ecosystem/inference-orchestrator/tests/test_orchestrator_helpers.py
  • python-ecosystem/inference-orchestrator/tests/test_prompt_dry_run.py
  • python-ecosystem/inference-orchestrator/tests/test_qa_documentation.py
  • python-ecosystem/inference-orchestrator/tests/test_queue_consumer.py
  • python-ecosystem/inference-orchestrator/tests/test_rag_client.py
  • python-ecosystem/inference-orchestrator/tests/test_review_service_helpers.py
  • python-ecosystem/inference-orchestrator/tests/test_stage_1_file_review.py
  • python-ecosystem/inference-orchestrator/tests/test_streaming_router_lifecycle.py
  • python-ecosystem/rag-pipeline/integration/test_pr_endpoints.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/api/models.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/api/routers/index.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/api/routers/pr.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/coordination.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/generation_manifest.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager/collection_manager.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager/indexer.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager/manager.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/core/revision_preflight.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/models/config.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/server/rag_queue_consumer.py
  • python-ecosystem/rag-pipeline/src/rag_pipeline/services/base.py
  • python-ecosystem/rag-pipeline/tests/test_api_app.py
  • python-ecosystem/rag-pipeline/tests/test_api_models.py
  • python-ecosystem/rag-pipeline/tests/test_config.py
  • python-ecosystem/rag-pipeline/tests/test_coordination.py
  • python-ecosystem/rag-pipeline/tests/test_generation_advance.py
  • python-ecosystem/rag-pipeline/tests/test_incremental_repository_overlay.py
  • python-ecosystem/rag-pipeline/tests/test_index_manager.py
  • python-ecosystem/rag-pipeline/tests/test_rag_queue_consumer.py
  • python-ecosystem/rag-pipeline/tests/test_revision_preflight.py
  • python-ecosystem/rag-pipeline/tests/test_router_index.py
  • python-ecosystem/rag-pipeline/tests/test_router_pr.py
  • python-ecosystem/rag-pipeline/tests/test_services.py
  • tools/review_quality/neutral_prompt_context_gate.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

The pull request adds structured QA-document parsing and public sharing across Python and Java services. It also adds RAG job ownership and recovery coordination, Jira URL-link rendering, scheduler configuration, Maven module wiring, and a frontend submodule update.

Changes

QA document generation and public sharing

Layer / File(s) Summary
QA content contracts and parser
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/*, java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/qadoc/*
Adds structured QA content records, marked and legacy section parsing, scenario extraction, environment extraction, and shareable section replacement.
Public-share token library
java-ecosystem/libs/public-share/*
Adds hashed opaque tokens, expiration and revocation, JPA persistence, token resolution, auto-configuration, migrations, and tests.
QA generation and handoff flow
python-ecosystem/inference-orchestrator/src/service/qa_documentation/*, python-ecosystem/inference-orchestrator/src/utils/prompts/constants_qa_doc.py, java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/*, java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java, java-ecosystem/services/pipeline-agent/src/test/*
Requires extractable test cases, repairs invalid generated sections, reuses eligible persisted documents, and posts public preview links after persistence succeeds.
Public QA preview API
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/*, java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/*, java-ecosystem/services/web-server/src/test/*
Adds token resolution, provider-based preview lookup, sanitized QA-document responses, workspace-aware route generation, and security-header tests.
Module and dependency wiring
java-ecosystem/pom.xml, java-ecosystem/services/pipeline-agent/pom.xml, java-ecosystem/services/web-server/pom.xml, java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/config/AsyncConfig.java, java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/config/AsyncConfigTest.java
Registers the public-share module and dependencies, and adds a configurable task scheduler with concurrency coverage.

RAG lifecycle coordination

Layer / File(s) Summary
RAG persistence and repository contracts
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/*, java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/*, java-ecosystem/libs/core/src/main/resources/db/migration/managed/*
Persists active job IDs and analysis lock keys. Adds pessimistic locking, operation-state queries, and operator-alias candidate projections.
RAG job ownership and build propagation
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/*, java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/*
Propagates job and lock identifiers through indexing, rejects stale status transitions, locks lifecycle updates, and restricts project status updates to base branches.
RAG recovery and alias reconciliation
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java, java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java, java-ecosystem/libs/rag-engine/src/test/*
Recovers abandoned operations, repairs failed active projections, preserves newer job ownership, releases exact locks, and reconciles aliases from repository projections.

Jira URL rendering

Layer / File(s) Summary
ADF URL link rendering
java-ecosystem/libs/task-management/src/main/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClient.java, java-ecosystem/libs/task-management/src/test/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClientTest.java
Converts bare HTTP(S) URLs into ADF links. It removes trailing sentence punctuation and unmatched closing delimiters while preserving balanced delimiters and fragments.

Frontend revision pointer

Layer / File(s) Summary
Frontend revision pointer
frontend
Updates the frontend submodule reference.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant QAOrchestrator
  participant QaAutoDocListener
  participant QaDocPublicPreviewService
  participant PublicShareLinkService
  participant JiraCloudClient
  QAOrchestrator->>QaAutoDocListener: return QA document
  QaAutoDocListener->>QaDocPublicPreviewService: create preview URL and task comment
  QaDocPublicPreviewService->>PublicShareLinkService: issue document share
  PublicShareLinkService-->>QaDocPublicPreviewService: frontend share URL
  QaDocPublicPreviewService-->>QaAutoDocListener: preview-link comment
  QaAutoDocListener->>JiraCloudClient: post task comment
  JiraCloudClient-->>JiraCloudClient: render URL as ADF link
Loading
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title identifies a release candidate but does not describe the main changes, which include QA document sharing and RAG recovery improvements. Use a specific title that names the primary change, such as "Add secure QA document previews and RAG recovery improvements".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 1.8.1-rc

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

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

⚠️ Outside diff range comments (1)
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java (1)

38-63: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize project-level start transitions before assigning ownership.

findByProjectIdForUpdate only locks an existing status row. Two starts can both observe no row and race into the unique constraint. A second start can also overwrite an active owner because both methods assign activeJobId without checking the current live state. Branch-scoped analysis locks do not prevent this for different branches of the same project.

  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java#L38-L63: serialize creation and reject a live status owned by a different job.
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java#L191-L201: apply the same ownership guard before changing to UPDATING.
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingServiceTest.java#L89-L130: add coverage for a conflicting active owner and concurrent first-status creation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java`
around lines 38 - 63, Serialize project-level start transitions in
RagIndexTrackingService.markIndexingStarted before creating or updating the
status, preventing concurrent first-status creation and rejecting a live status
owned by a different activeJobId. Apply the same ownership guard in the UPDATING
transition around the sibling service site. Add tests in
RagIndexTrackingServiceTest covering conflicting active ownership and concurrent
creation of the first project status; update the listed service sites and test
range accordingly.
🧹 Nitpick comments (9)
java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImplTest.java (1)

805-805: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stub the job ID so the assertion proves ownership propagation.

mockJob.getId() is not stubbed, so the expected 0L is the Mockito default for a primitive return. The assertion passes even if the production code passes a different unstubbed job. Stub a distinct ID and verify that value.

♻️ Proposed change
-        verify(ragIndexTrackingService).markUpdatingCompleted(
-                testProject, "main", "current-head", 0, 0, null, 0L);
+        verify(ragIndexTrackingService).markUpdatingCompleted(
+                testProject, "main", "current-head", 0, 0, null, 4242L);

Add the stub next to the other mockJob setup:

when(mockJob.getId()).thenReturn(4242L);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImplTest.java`
at line 805, Update the mockJob setup in RagOperationsServiceImplTest to stub
getId() with a distinct value such as 4242L, then change the expected job ID in
the assertion to that value so the test verifies ownership propagation.
java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryServiceTest.java (1)

257-273: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the positive case for failIfAbandoned.

This test covers only the rejection path. No test asserts that a stale operation returns true and transitions the operation and its generation to FAILED. Add a case with updatedAt older than the cutoff. Also add a case where the status is already SUCCEEDED to confirm the method returns false.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryServiceTest.java`
around lines 257 - 273, Extend the failIfAbandoned tests around
abandonmentClaimRechecksAHeartbeatUnderTheOperationLock with a stale operation
case that verifies true is returned and both the operation and generation
transition to FAILED, plus a SUCCEEDED-status case verifying false is returned.
Reuse the existing repository stubbing and assertions for failIfAbandoned, and
keep the current heartbeat rejection test unchanged.
java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java (1)

24-45: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider @ExtendWith(MockitoExtension.class) for consistency.

The sibling test RagBranchIndexRegistryServiceTest uses the Mockito extension with @Mock fields and lenient() where needed. This class creates mocks manually, so strict stubbing does not apply and unused stubs stay undetected. Aligning the two classes would have surfaced the missing findById stub noted above.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java`
around lines 24 - 45, Update RagIndexOperationRecoveryServiceTest to use
MockitoExtension with `@Mock` fields instead of creating mocks manually in setUp,
and remove the corresponding mock initializations. Preserve the recovery
construction while adding lenient() only for stubs that are intentionally
unused, so strict stubbing detects missing interactions such as findById.
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java (1)

39-48: 🚀 Performance & Scalability | 🔵 Trivial

Consider bounding the reconciliation scan.

The loop performs one pipeline call per candidate with no limit. On a large installation a single scheduled run can exceed the 5-minute fixed delay. Consider a page limit per run, or a metric for candidate count and run duration, so the backlog stays observable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java`
around lines 39 - 48, Bound the reconciliation work in
RagBranchOperatorAliasReconciliationService by limiting the candidates processed
per scheduled run, while preserving the existing publishGenerationAliases
behavior for each selected candidate. Use an established page-size or batch-size
configuration if available; otherwise introduce a clear bounded limit and expose
candidate-count or run-duration metrics so remaining backlog stays observable.
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java (1)

35-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider a narrower dependency than RagOperationsService.

The recovery scheduler injects the full RagOperationsService only to call the default method getBaseBranch. This couples a background recovery component to the RAG orchestration service and creates a future bean-cycle risk, because RagOperationsServiceImpl already depends on RagBranchIndexRegistryService. Extract base-branch resolution into a small stateless helper and inject that instead.

Also applies to: 116-121

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java`
at line 35, Replace the RagOperationsService dependency in
RagIndexOperationRecoveryService with a small stateless helper dedicated to
base-branch resolution, and update the recovery scheduling logic around
getBaseBranch to use that helper. Ensure the helper preserves the existing
default getBaseBranch behavior, then remove the broad orchestration-service
injection to avoid the dependency cycle.
java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryService.java (1)

262-267: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove or wire hasLiveOperation.

hasLiveOperation has no callers, and existsByProjectIdAndBranchNameAndStatusIn is used only by this method. Remove both APIs, or use hasLiveOperation in the recovery flow.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryService.java`
around lines 262 - 267, Remove the unused hasLiveOperation method from
RagBranchIndexRegistryService and remove the corresponding
existsByProjectIdAndBranchNameAndStatusIn repository API, unless the recovery
flow is updated to call hasLiveOperation. Do not leave the repository query
without a caller.
java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParserTest.java (1)

80-109: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the exception contracts of replaceShareableSections.

replaceMarkedTestCaseBody throws IllegalArgumentException when the marked section is absent or when the replacement is blank. replaceEnvironmentBody throws for a blank environment replacement. The QA handoff flow depends on these throws, but no test asserts them.

Add negative tests for the three throw paths. Also cover the synthetic-heading branch, where the markers exist but contain no Test Scenarios heading.

🧪 Proposed tests
`@Test`
void rejectsReplacementWhenTheMarkedSectionIsAbsent() {
    assertThatThrownBy(() -> QaDocContentParser.replaceShareableSections(
            "### 1. Change Summary\nNo markers here.\n", "https://x/#tab=a", "https://x/#tab=b"))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("marked QA test-case section");
}

`@Test`
void rejectsBlankReplacements() {
    String markdown = """
            <!-- codecrow-test-cases:start -->
            ### 3. Test Scenarios
            **Pay** (HIGH)
            - **Expected Result:** Ok.
            <!-- codecrow-test-cases:end -->
            """;
    assertThatThrownBy(() -> QaDocContentParser.replaceShareableSections(markdown, "  ", "https://x/#tab=b"))
            .isInstanceOf(IllegalArgumentException.class);
    assertThatThrownBy(() -> QaDocContentParser.replaceShareableSections(markdown, "https://x/#tab=a", "  "))
            .isInstanceOf(IllegalArgumentException.class);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParserTest.java`
around lines 80 - 109, Add negative tests for replaceShareableSections covering
missing marked test-case sections, blank test-case replacements, and blank
environment replacements, asserting IllegalArgumentException and the existing
missing-section message where applicable. Also add coverage for the
synthetic-heading branch by supplying markers without a Test Scenarios heading
and verifying the replacement behavior.
java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocHandoffRetryPolicyTest.java (1)

13-20: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

Make task-ID casing explicit in the test

QaDocHandoffRetryPolicy.shouldReuse compares task IDs case-insensitively. Use "TASK-1" in this commit/timestamp test and add a separate test named for case-insensitive task matching.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocHandoffRetryPolicyTest.java`
around lines 13 - 20, Update
reusesCurrentCommitDocumentCreatedAfterTheLastSuccessfulHandoff to pass "TASK-1"
and make the case-insensitive task-ID behavior explicit; add a separate test
with a name indicating case-insensitive task matching that verifies shouldReuse
accepts task IDs differing only by casing.
java-ecosystem/services/web-server/src/test/java/org/rostilos/codecrow/webserver/publicshare/PublicShareControllerTest.java (1)

52-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover the unsupported resource-type branch.

This test only covers an unresolved token. It does not cover a resolved token whose resourceType has no registered provider. Add that case before keeping the combined test name.

Proposed test addition
+        when(links.resolve("unsupported"))
+                .thenReturn(Optional.of(new ResolvedPublicShare("unknown-preview", "internal-9")));
+
+        assertThat(controller.resolvePublicPreview(
+                new PublicShareResolveRequest("unsupported"), null)
+                .getStatusCode().value()).isEqualTo(404);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/services/web-server/src/test/java/org/rostilos/codecrow/webserver/publicshare/PublicShareControllerTest.java`
around lines 52 - 62, Extend
usesTheSameNotFoundResponseForInvalidAndUnsupportedTokens to also stub a
resolved public-share token with an unregistered resourceType, then assert
resolvePublicPreview returns 404 and no provider preview method is called. Keep
the existing invalid-token assertions and combined test coverage.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java`:
- Around line 29-33: Update ENVIRONMENT_SECTION_HEADING so the numeric heading
alternative matches only titles containing the approved environment/setup names,
rather than accepting any heading numbered 6. Preserve case-insensitive matching
and the existing unnumbered/numerically prefixed title variants, ensuring
headings such as “6. Regression Risks” are not classified as the environment
section.

In
`@java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.27.0__rag_index_active_job.sql`:
- Around line 7-8: Update the idx_rag_index_status_active_job migration to use
CREATE INDEX CONCURRENTLY, add the corresponding migration configuration with
executeInTransaction=false, and configure the Flyway run with
flyway.postgresql.transactional.lock=false.

In `@java-ecosystem/libs/public-share/pom.xml`:
- Around line 38-40: Update the managed AssertJ version in the parent
java-ecosystem/pom.xml dependency management to 3.27.7 or later, so the
test-scoped assertj-core dependency inherits the secure version. Do not change
the dependency declaration in public-share.

In
`@java-ecosystem/libs/public-share/src/test/java/org/rostilos/codecrow/publicshare/service/PublicShareLinkServiceTest.java`:
- Around line 52-57: Update
rejectsValuesThatAreNotPublicShareTokensWithoutQueryingByRawValue to call
verifyNoInteractions(repository) after both resolve assertions, and add the
corresponding static import so the test verifies the repository was not queried.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java`:
- Around line 80-86: Update lock recovery around recoverProjections and all
lock-backed startBuild overloads so every started operation persists the exact
analysisLockKey, including the overload that currently permits null. Adjust the
lock lookup used during recovery to require the lock’s expiration timestamp to
be in the future, preventing expired rows from matching before cleanup.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java`:
- Around line 152-169: Update
alreadyFailedOperationPreservesStatusOwnedByANewerJob to stub jobs.findById(91L)
with the expected job result before invoking recovery. Use
operation.getJobId()’s 91L value so failDurableJob follows its intended success
path instead of triggering and swallowing a NullPointerException.

In
`@java-ecosystem/libs/task-management/src/main/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClient.java`:
- Around line 751-770: Update trimBareUrlEnd to treat a trailing double quote as
removable punctuation alongside .,;!? so quoted bare URLs produce an href
without the closing quote. Add a regression test covering
"https://example.test/path" and verify the generated URL excludes the quote.

In
`@java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java`:
- Around line 602-611: Update isPublicPreviewOnlyComment to remove
COMMENT_MARKER and its PR-tracking variants along with the preview URL and label
before checking blank content. Preserve classification as true for comments
containing only an auto-document marker and preview link, and add a test
covering <!-- codecrow-qa-autodoc --> before the preview link.

In
`@java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java`:
- Around line 39-51: The preview flow currently validates one document but
renders another, allowing a token to be issued before rendering fails. Update
buildTaskComment to accept and render the same QaDocDocument instance passed to
createPreviewUrl, and adjust its callers so persistedDocument is used
consistently for both operations.

In
`@java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProvider.java`:
- Around line 135-139: Update hasCompatibleTaskKey to return true only when both
the normalized analysis task key and documentTaskKey are non-null and equal;
return false for either missing key. Add a regression test covering a missing
document task key or analysis task key and verify the task summary is not
exposed.

In
`@python-ecosystem/inference-orchestrator/src/service/qa_documentation/qa_doc_orchestrator.py`:
- Around line 726-775: Update _ensure_test_cases to reject repair responses that
omit the codecrow test-case markers instead of wrapping the entire generated
response in a fabricated section. Require both valid start and end markers,
extract only that marked section, and raise the existing repair failure error
when either marker is missing; preserve the structured-scenario validation for
marked output.

---

Outside diff comments:
In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java`:
- Around line 38-63: Serialize project-level start transitions in
RagIndexTrackingService.markIndexingStarted before creating or updating the
status, preventing concurrent first-status creation and rejecting a live status
owned by a different activeJobId. Apply the same ownership guard in the UPDATING
transition around the sibling service site. Add tests in
RagIndexTrackingServiceTest covering conflicting active ownership and concurrent
creation of the first project status; update the listed service sites and test
range accordingly.

---

Nitpick comments:
In
`@java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParserTest.java`:
- Around line 80-109: Add negative tests for replaceShareableSections covering
missing marked test-case sections, blank test-case replacements, and blank
environment replacements, asserting IllegalArgumentException and the existing
missing-section message where applicable. Also add coverage for the
synthetic-heading branch by supplying markers without a Test Scenarios heading
and verifying the replacement behavior.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java`:
- Around line 39-48: Bound the reconciliation work in
RagBranchOperatorAliasReconciliationService by limiting the candidates processed
per scheduled run, while preserving the existing publishGenerationAliases
behavior for each selected candidate. Use an established page-size or batch-size
configuration if available; otherwise introduce a clear bounded limit and expose
candidate-count or run-duration metrics so remaining backlog stays observable.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java`:
- Line 35: Replace the RagOperationsService dependency in
RagIndexOperationRecoveryService with a small stateless helper dedicated to
base-branch resolution, and update the recovery scheduling logic around
getBaseBranch to use that helper. Ensure the helper preserves the existing
default getBaseBranch behavior, then remove the broad orchestration-service
injection to avoid the dependency cycle.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryService.java`:
- Around line 262-267: Remove the unused hasLiveOperation method from
RagBranchIndexRegistryService and remove the corresponding
existsByProjectIdAndBranchNameAndStatusIn repository API, unless the recovery
flow is updated to call hasLiveOperation. Do not leave the repository query
without a caller.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java`:
- Around line 24-45: Update RagIndexOperationRecoveryServiceTest to use
MockitoExtension with `@Mock` fields instead of creating mocks manually in setUp,
and remove the corresponding mock initializations. Preserve the recovery
construction while adding lenient() only for stubs that are intentionally
unused, so strict stubbing detects missing interactions such as findById.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryServiceTest.java`:
- Around line 257-273: Extend the failIfAbandoned tests around
abandonmentClaimRechecksAHeartbeatUnderTheOperationLock with a stale operation
case that verifies true is returned and both the operation and generation
transition to FAILED, plus a SUCCEEDED-status case verifying false is returned.
Reuse the existing repository stubbing and assertions for failIfAbandoned, and
keep the current heartbeat rejection test unchanged.

In
`@java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImplTest.java`:
- Line 805: Update the mockJob setup in RagOperationsServiceImplTest to stub
getId() with a distinct value such as 4242L, then change the expected job ID in
the assertion to that value so the test verifies ownership propagation.

In
`@java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocHandoffRetryPolicyTest.java`:
- Around line 13-20: Update
reusesCurrentCommitDocumentCreatedAfterTheLastSuccessfulHandoff to pass "TASK-1"
and make the case-insensitive task-ID behavior explicit; add a separate test
with a name indicating case-insensitive task matching that verifies shouldReuse
accepts task IDs differing only by casing.

In
`@java-ecosystem/services/web-server/src/test/java/org/rostilos/codecrow/webserver/publicshare/PublicShareControllerTest.java`:
- Around line 52-62: Extend
usesTheSameNotFoundResponseForInvalidAndUnsupportedTokens to also stub a
resolved public-share token with an unregistered resourceType, then assert
resolvePublicPreview returns 404 and no provider preview method is called. Keep
the existing invalid-token assertions and combined test coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 596b502c-9236-4ece-ba82-6980cef9550f

📥 Commits

Reviewing files that changed from the base of the PR and between 43acce3 and a84e9e6.

📒 Files selected for processing (75)
  • frontend
  • java-ecosystem/libs/core/src/main/java/module-info.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/analysis/RagIndexStatus.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/rag/RagIndexOperation.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/analysis/RagIndexStatusRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/rag/RagBranchIndexRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/rag/RagIndexOperationRepository.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/QaDocDocumentService.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocContent.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocPublicShareResource.java
  • java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocTestCase.java
  • java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.27.0__rag_index_active_job.sql
  • java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.28.0__rag_operation_lock_owner.sql
  • java-ecosystem/libs/core/src/test/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParserTest.java
  • java-ecosystem/libs/public-share/pom.xml
  • java-ecosystem/libs/public-share/src/main/java/module-info.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/api/IssuedPublicShare.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/api/ResolvedPublicShare.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/config/PublicShareAutoConfiguration.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/model/PublicShareLink.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/persistence/PublicShareLinkRepository.java
  • java-ecosystem/libs/public-share/src/main/java/org/rostilos/codecrow/publicshare/service/PublicShareLinkService.java
  • java-ecosystem/libs/public-share/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  • java-ecosystem/libs/public-share/src/main/resources/db/migration/managed/V2.25.0__public_share_links.sql
  • java-ecosystem/libs/public-share/src/main/resources/db/migration/managed/V2.26.0__rename_qa_document_share_resource.sql
  • java-ecosystem/libs/public-share/src/test/java/org/rostilos/codecrow/publicshare/config/PublicShareAutoConfigurationContextTest.java
  • java-ecosystem/libs/public-share/src/test/java/org/rostilos/codecrow/publicshare/config/PublicShareAutoConfigurationTest.java
  • java-ecosystem/libs/public-share/src/test/java/org/rostilos/codecrow/publicshare/service/PublicShareLinkServiceTest.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/BranchIndexGenerationBuildService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/BranchIndexMaintenanceService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImpl.java
  • java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/VcsRagIndexingService.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/BranchIndexGenerationBuildServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagBranchOperatorAliasReconciliationServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagBranchIndexRegistryServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingServiceTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/RagOperationsServiceImplTest.java
  • java-ecosystem/libs/rag-engine/src/test/java/org/rostilos/codecrow/ragengine/service/VcsRagIndexingServiceTest.java
  • java-ecosystem/libs/task-management/src/main/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClient.java
  • java-ecosystem/libs/task-management/src/test/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClientTest.java
  • java-ecosystem/pom.xml
  • java-ecosystem/services/pipeline-agent/pom.xml
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/config/AsyncConfig.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocGenerationContext.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocGenerationService.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocHandoffRetryPolicy.java
  • java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/config/AsyncConfigTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessorTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListenerTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocHandoffRetryPolicyTest.java
  • java-ecosystem/services/pipeline-agent/src/test/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewServiceTest.java
  • java-ecosystem/services/web-server/pom.xml
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/QaDocDocumentResponse.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/QaDocTestCaseResponse.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/PublicShareController.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/PublicSharePreviewResponse.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/PublicShareResolveRequest.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/PublicShareResourceProvider.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocPublicPreview.java
  • java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProvider.java
  • java-ecosystem/services/web-server/src/test/java/org/rostilos/codecrow/webserver/publicshare/PublicShareControllerTest.java
  • java-ecosystem/services/web-server/src/test/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProviderTest.java
  • python-ecosystem/inference-orchestrator/src/api/routers/qa_documentation.py
  • python-ecosystem/inference-orchestrator/src/service/qa_documentation/qa_doc_orchestrator.py
  • python-ecosystem/inference-orchestrator/src/utils/prompts/constants_qa_doc.py
  • python-ecosystem/inference-orchestrator/tests/test_qa_documentation.py

Comment on lines +29 to +33
private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
"^(?:6\\.\\s+.+|(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"
+ "Setup and Environment Notes|Environment Setup Notes|Environment Notes|Setup Notes))$",
Pattern.CASE_INSENSITIVE
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restrict the numeric alternative in ENVIRONMENT_SECTION_HEADING.

The first alternative 6\.\s+.+ matches any heading numbered 6, regardless of its title. A document that numbers a different section as 6 (for example ## 6. Regression Risks) is then classified as the environment section. Two effects follow: parse removes that content from overviewMarkdown and returns it as environmentMarkdown, and replaceShareableSections replaces that body with the environment preview link in the Jira comment.

Require the title to name environment or setup content.

🔧 Proposed tightening
     private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
-            "^(?:6\\.\\s+.+|(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"
+            "^(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"
                     + "Setup and Environment Notes|Environment Setup Notes|Environment Notes|Setup Notes))$",
             Pattern.CASE_INSENSITIVE
     );
📝 Committable suggestion

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

Suggested change
private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
"^(?:6\\.\\s+.+|(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"
+ "Setup and Environment Notes|Environment Setup Notes|Environment Notes|Setup Notes))$",
Pattern.CASE_INSENSITIVE
);
private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
"^(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"
"Setup and Environment Notes|Environment Setup Notes|Environment Notes|Setup Notes))$",
Pattern.CASE_INSENSITIVE
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java`
around lines 29 - 33, Update ENVIRONMENT_SECTION_HEADING so the numeric heading
alternative matches only titles containing the approved environment/setup names,
rather than accepting any heading numbered 6. Preserve case-insensitive matching
and the existing unnumbered/numerically prefixed title variants, ensuring
headings such as “6. Regression Risks” are not classified as the environment
section.

Comment on lines +7 to +8
CREATE INDEX IF NOT EXISTS idx_rag_index_status_active_job
ON rag_index_status(active_job_id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 3 'Flyway|executeInTransaction|CREATE INDEX CONCURRENTLY' java-ecosystem

Repository: rostilos/CodeCrow

Length of output: 4839


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- migration files ---'
fd -i -t f 'V2.27.0__rag_index_active_job.sql|.*flyway.*|pom.xml|application.*properties|application.*yml' java-ecosystem \
  | sort

printf '%s\n' '--- target migration and nearby migrations ---'
target='java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.27.0__rag_index_active_job.sql'
cat -n "$target"
printf '%s\n' '--- managed migrations containing index creation ---'
rg -n -C 3 'CREATE[[:space:]]+(UNIQUE[[:space:]]+)?INDEX|CONCURRENTLY|transaction' \
  java-ecosystem/libs/core/src/main/resources/db/migration/managed

printf '%s\n' '--- Flyway configuration and dependency declarations ---'
rg -n -C 4 'flyway|Flyway|flyway-core|spring.flyway' java-ecosystem \
  -g 'pom.xml' -g '*.properties' -g '*.yml' -g '*.yaml' -g '*.java' \
  | head -n 400

Repository: rostilos/CodeCrow

Length of output: 39005


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- core application configuration ---'
cat -n java-ecosystem/libs/core/src/main/resources/application.yml

printf '%s\n' '--- root build and Spring Boot versions ---'
sed -n '1,130p' java-ecosystem/pom.xml
rg -n -C 5 'spring-boot|flyway|maven.compiler|java.version' java-ecosystem/pom.xml \
  java-ecosystem/libs/core/pom.xml \
  java-ecosystem/services/web-server/pom.xml \
  java-ecosystem/services/pipeline-agent/pom.xml

printf '%s\n' '--- migration runner code and configuration properties ---'
rg -n -C 5 'Flyway|migrate\(|spring.flyway|flyway.locations|locations:.*migration|baseline' \
  java-ecosystem -g '*.java' -g '*.yml' -g '*.yaml' -g '*.properties' -g 'pom.xml'

Repository: rostilos/CodeCrow

Length of output: 38623


🌐 Web query:

Flyway 9.22.3 PostgreSQL CREATE INDEX CONCURRENTLY automatically runs outside transaction SQL migration

💡 Result:

No, Flyway does not automatically run CREATE INDEX CONCURRENTLY outside a transaction in a way that avoids potential deadlocks or hangs. While you can mark a migration to execute outside a transaction (using executeInTransaction=false), Flyway's default behavior in PostgreSQL is to acquire a transactional advisory lock to maintain state consistency, which can conflict with the non-transactional requirements of CREATE INDEX CONCURRENTLY [1][2][3]. When a migration script containing CREATE INDEX CONCURRENTLY runs, it often results in the migration process hanging or deadlocking because the transactional lock holds a connection open in a transaction state, while the concurrent index build itself requires its own independent transaction management [2][4][5]. To successfully run CREATE INDEX CONCURRENTLY in Flyway, you must typically perform the following: 1. Configure the transactional lock setting: You must set flyway.postgresql.transactional.lock to false [6][1]. This switches Flyway from using transactional advisory locks to session-level advisory locks, which allows concurrent index operations to proceed without holding an open transaction [6][1]. 2. Configuration scope: Note that flyway.postgresql.transactional.lock is a global configuration parameter [2]. Setting it to false affects all migrations in that execution run, not just the specific script containing the concurrent index [2]. While some users have reported issues where this setting does not resolve hangs in specific environments or newer versions [7][8], it remains the primary documented solution for this requirement [6][1][5].

Citations:


🌐 Web query:

site:documentation.red-gate.com/flyway/reference/database-driver-reference/postgresql-database transactional lock CREATE INDEX CONCURRENTLY Flyway 9

💡 Result:

In Flyway, attempting to run a CREATE INDEX CONCURRENTLY command within a standard transactional migration will cause issues because PostgreSQL does not permit CREATE INDEX CONCURRENTLY to be executed inside a transaction block [1]. By default, Flyway wraps migrations in a transactional lock for PostgreSQL [1]. To successfully use CREATE INDEX CONCURRENTLY, you must disable this default behavior by setting the configuration property flyway.postgresql.transactional.lock=false [1]. This change switches Flyway from using a transactional lock to a session-level lock, allowing the CREATE INDEX CONCURRENTLY statement to execute outside of a transaction [1].

Citations:


🌐 Web query:

site:documentation.red-gate.com/flyway/flyway-concepts/migrations/migration-transaction-handling executeInTransaction SQL migration PostgreSQL

💡 Result:

In the context of database migration tools like Flyway, executeInTransaction is a configuration setting or method used to control whether a migration script is executed within a database transaction [1]. PostgreSQL supports transactional Data Definition Language (DDL), meaning most schema changes can be wrapped in a transaction, allowing them to be rolled back if an error occurs [1]. However, some specific PostgreSQL commands cannot be executed within a transaction block because they perform operations that are incompatible with transactional integrity, such as creating or dropping databases, tablespaces, or performing certain types of administrative maintenance [1]. Key considerations include: 1. Transactional DDL: Since PostgreSQL allows most DDL statements inside transactions, running migrations in a transaction is generally recommended to ensure atomic updates [1]. 2. Limitations: If a migration script contains statements that forbid transactional execution, the migration tool must be configured to either run the migration outside of a transaction or handle the mixed mode appropriately [1]. 3. Configuration: Tools like Flyway provide mechanisms to explicitly define this behavior. For SQL migrations, you can configure the executeInTransaction property for a specific script. For Java-based migrations, you can override the canExecuteInTransaction method within the migration class to return false if the logic requires non-transactional execution [1]. 4. Mixed Mode: If your migration suite contains a mix of transactional and non-transactional statements, you may need to enable specific settings (such as a mixed property in Flyway) to allow them to coexist in the same migration run, depending on the tool's capabilities [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- available local Flyway artifacts ---'
find "${HOME}/.m2/repository/org/flywaydb" -maxdepth 5 -type f 2>/dev/null | sort || true

printf '%s\n' '--- migration location declarations ---'
rg -n -C 4 'db/migration|locations|flyway' java-ecosystem \
  -g '*.yml' -g '*.yaml' -g '*.properties' -g '*.java' -g 'pom.xml'

printf '%s\n' '--- all explicit transaction-related Flyway settings ---'
rg -n -i -C 3 'execute.?in.?transaction|mixed|transactional.?lock|lock.?retry' java-ecosystem || true

Repository: rostilos/CodeCrow

Length of output: 37906


🌐 Web query:

Flyway SQL migration metadata executeInTransaction=false comment syntax

💡 Result:

Flyway does not use SQL comment syntax to set executeInTransaction=false [1][2]. Instead, you must use a separate script configuration file [3][4]. To execute a specific SQL migration without a transaction, perform the following steps: 1. Create a configuration file in the same directory as your SQL migration file [4]. 2. The configuration file must have the exact same filename as your SQL migration, but with an added.conf extension (e.g., if your migration is V1migration.sql, the config file should be V1migration.sql.conf) [5][4]. 3. Add the following line to this.conf file [6][7]: executeInTransaction=false This approach allows you to control transaction handling on a per-script basis without modifying the SQL content itself [3][4]. This is particularly useful for database systems like PostgreSQL or SQL Server where certain commands cannot execute within a transaction block [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

root = Path("java-ecosystem")
migration = root / "libs/core/src/main/resources/db/migration/managed/V2.27.0__rag_index_active_job.sql"
sql = migration.read_text()

statements = [
    s.strip()
    for s in re.split(r";\s*", sql)
    if s.strip() and not all(not line.strip() or line.lstrip().startswith("--")
                             for line in s.splitlines())
]

print("migration:", migration)
print("statements:", len(statements))
for i, statement in enumerate(statements, 1):
    normalized = re.sub(r"\s+", " ", statement)
    print(f"{i}: {normalized}")

print("ordinary_index:", bool(re.search(
    r"\bCREATE\s+INDEX\b(?!\s+CONCURRENTLY\b)", sql, re.I)))
print("concurrent_index:", bool(re.search(
    r"\bCREATE\s+INDEX\s+CONCURRENTLY\b", sql, re.I)))

sidecar = migration.with_name(migration.name + ".conf")
print("sidecar_exists:", sidecar.exists(), sidecar)

settings = []
for path in root.rglob("*"):
    if path.is_file() and path.suffix.lower() in {".properties", ".yml", ".yaml", ".conf", ".xml"}:
        text = path.read_text(errors="replace")
        if re.search(r"(?:spring\.flyway|flyway\.(?:executeInTransaction|postgresql\.transactional\.lock)|executeInTransaction)", text, re.I):
            settings.append(str(path))
print("transaction_setting_files:", settings)
PY

Repository: rostilos/CodeCrow

Length of output: 1042


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
import urllib.request

api = "https://api.github.com/repos/flyway/flyway/git/trees/flyway-9.22.3?recursive=1"
request = urllib.request.Request(api, headers={"Accept": "application/vnd.github+json", "User-Agent": "readonly-review-check"})
with urllib.request.urlopen(request, timeout=20) as response:
    tree = json.load(response)["tree"]

paths = [
    item["path"] for item in tree
    if any(term in item["path"].lower() for term in (
        "postgresqldatabase", "postgresqlconfiguration", "sqlscript", "scriptconfiguration"
    ))
]
for path in paths:
    print(path)
PY

Repository: rostilos/CodeCrow

Length of output: 2441


Use a non-blocking index build for rag_index_status.

Replace CREATE INDEX with CREATE INDEX CONCURRENTLY. Add V2.27.0__rag_index_active_job.sql.conf with executeInTransaction=false, and set flyway.postgresql.transactional.lock=false for the Flyway run.

🧰 Tools
🪛 Squawk (2.61.0)

[warning] 7-8: During normal index creation, table updates are blocked, but reads are still allowed. Use concurrently to avoid blocking writes.

(require-concurrent-index-creation)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/core/src/main/resources/db/migration/managed/V2.27.0__rag_index_active_job.sql`
around lines 7 - 8, Update the idx_rag_index_status_active_job migration to use
CREATE INDEX CONCURRENTLY, add the corresponding migration configuration with
executeInTransaction=false, and configure the Flyway run with
flyway.postgresql.transactional.lock=false.

Source: Linters/SAST tools

Comment on lines +38 to +40
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Which AssertJ Core version first fixes GHSA-rqfh-9r24-8c9r?

💡 Result:

AssertJ Core version 3.27.7 is the first version that fixes the XXE vulnerability identified by GHSA-rqfh-9r24-8c9r (also tracked as CVE-2026-24400) [1][2][3]. This vulnerability existed in the org.assertj.core.util.xml.XmlStringPrettyFormatter class, which was used by the isXmlEqualTo(CharSequence) assertion [1][4]. Users of affected versions are advised to upgrade to 3.27.7, replace the affected assertion with XMLUnit, or avoid using the vulnerable methods with untrusted XML input [1][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- matching POM files ---'
git ls-files '*pom.xml' | head -80
printf '%s\n' '--- AssertJ declarations and managed versions ---'
rg -n -C 4 'assertj-core|assertj.version|org.assertj' --glob 'pom.xml' .
printf '%s\n' '--- target POM context ---'
python3 - <<'PY'
from pathlib import Path
p = Path("java-ecosystem/libs/public-share/pom.xml")
if p.exists():
    lines = p.read_text().splitlines()
    for i in range(max(0, 32), min(len(lines), 46)):
        print(f"{i+1:4}: {lines[i]}")
else:
    print("target POM not found")
PY

Repository: rostilos/CodeCrow

Length of output: 18902


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- AssertJ XML assertion usages ---'
rg -n -C 3 'isXmlEqualTo|isXml|XmlStringPrettyFormatter|assertj-core' \
  --glob '*.{java,kt,groovy,xml}' java-ecosystem analysis-plugins || true

printf '%s\n' '--- POM parent and AssertJ scope facts ---'
python3 - <<'PY'
from pathlib import Path
import xml.etree.ElementTree as ET

NS = {'m': 'http://maven.apache.org/POM/4.0.0'}
paths = [
    Path("java-ecosystem/pom.xml"),
    Path("java-ecosystem/libs/public-share/pom.xml"),
    Path("java-ecosystem/libs/test-support/pom.xml"),
]
for path in paths:
    root = ET.parse(path).getroot()
    parent = root.find("m:parent", NS)
    print(path)
    if parent is not None:
        print("  parent:", parent.findtext("m:groupId", namespaces=NS),
              parent.findtext("m:artifactId", namespaces=NS),
              parent.findtext("m:version", namespaces=NS),
              "relativePath=", parent.findtext("m:relativePath", namespaces=NS))
    for dep in root.findall(".//m:dependency", NS):
        if dep.findtext("m:artifactId", namespaces=NS) == "assertj-core":
            print("  assertj:", {
                "groupId": dep.findtext("m:groupId", namespaces=NS),
                "version": dep.findtext("m:version", namespaces=NS),
                "scope": dep.findtext("m:scope", namespaces=NS),
            })
PY

printf '%s\n' '--- relevant source files ---'
git ls-files 'java-ecosystem/**/*.java' 'analysis-plugins/**/*.java' |
  xargs -r rg -l 'isXmlEqualTo|XmlStringPrettyFormatter' || true

Repository: rostilos/CodeCrow

Length of output: 11359


🌐 Web query:

GHSA-rqfh-9r24-8c9r AssertJ Core affected versions fixed version official advisory

💡 Result:

The security advisory GHSA-rqfh-9r24-8c9r addresses an XML External Entity (XXE) vulnerability in AssertJ Core, identified as CVE-2026-24400 [1][2]. Affected Versions: The vulnerability affects AssertJ Core versions from 1.4.0 up to and including 3.27.6 [1][3][4]. Fixed Version: The issue is resolved in version 3.27.7 [1][5]. Advisory Details: The vulnerability exists in org.assertj.core.util.xml.XmlStringPrettyFormatter because the toXmlDocument(String) method initializes DocumentBuilderFactory with default settings, failing to disable DTDs or external entities [1][2][6]. This formatter is utilized by the isXmlEqualTo(CharSequence) assertion [1][6]. The advisory notes that applications are only vulnerable if they use untrusted XML input with either the isXmlEqualTo(CharSequence) method (from org.assertj.core.api.AbstractCharSequenceAssert) or the xmlPrettyFormat(String) method (from org.assertj.core.util.xml.XmlStringPrettyFormatter) [1][2][4]. Recommendations: Users of affected versions are advised to take one of the following actions, listed in order of preference: 1. Replace isXmlEqualTo(CharSequence) with XMLUnit [1][2]. 2. Upgrade to version 3.27.7 [1][6]. 3. Avoid using isXmlEqualTo(CharSequence) or XmlStringPrettyFormatter with untrusted input [1][4]. Additionally, the isXmlEqualTo(CharSequence) assertion was deprecated in version 3.18.0 in favor of XMLUnit and is scheduled for removal in version 4.0 [1][2][6]. In version 3.27.7, XmlStringPrettyFormatter has also been deprecated [1][2].

Citations:


Update the managed AssertJ version in java-ecosystem/pom.xml.

AssertJ 3.25.3 is affected by GHSA-rqfh-9r24-8c9r. Upgrade to 3.27.7 or later. Exploitation requires untrusted XML passed to the affected XML assertion methods.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@java-ecosystem/libs/public-share/pom.xml` around lines 38 - 40, Update the
managed AssertJ version in the parent java-ecosystem/pom.xml dependency
management to 3.27.7 or later, so the test-scoped assertj-core dependency
inherits the secure version. Do not change the dependency declaration in
public-share.

Source: Linters/SAST tools

Comment on lines +52 to +57
void rejectsValuesThatAreNotPublicShareTokensWithoutQueryingByRawValue() {
PublicShareLinkService service = new PublicShareLinkService(repository, secureRandom);

assertThat(service.resolve("eyJhbGciOiJIUzI1NiJ9.jwt.payload")).isEmpty();
assertThat(service.resolve("ccs_short")).isEmpty();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

test_file=$(fd -t f 'PublicShareLinkServiceTest\.java$' . | head -n 1)
service_file=$(fd -t f 'PublicShareLinkService\.java$' . | head -n 1)

printf '%s\n' "TEST_FILE=$test_file"
printf '%s\n' "SERVICE_FILE=$service_file"

printf '%s\n' '--- test outline ---'
ast-grep outline "$test_file" || true

printf '%s\n' '--- service outline ---'
ast-grep outline "$service_file" || true

printf '%s\n' '--- test setup and target test ---'
rg -n -C 18 'repository|secureRandom|rejectsValuesThatAreNotPublicShareTokensWithoutQueryingByRawValue|findByTokenHash|verifyNoInteractions|verify\(' "$test_file"

printf '%s\n' '--- service token resolution methods ---'
rg -n -C 25 'resolve|findByTokenHash|repository' "$service_file"

Repository: rostilos/CodeCrow

Length of output: 7985


Add verifyNoInteractions(repository) after both assertions.

The assertions alone do not prove that resolve skipped the repository. Add the corresponding static import.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/public-share/src/test/java/org/rostilos/codecrow/publicshare/service/PublicShareLinkServiceTest.java`
around lines 52 - 57, Update
rejectsValuesThatAreNotPublicShareTokensWithoutQueryingByRawValue to call
verifyNoInteractions(repository) after both resolve assertions, and add the
corresponding static import so the test verifies the repository was not queried.

Comment on lines +80 to +86
for (var operation : registryService.findFailedOperationsWithActiveProjections()) {
String diagnostic = operation.getErrorMessage() != null
&& !operation.getErrorMessage().isBlank()
? operation.getErrorMessage()
: "RAG generation failed before its durable projections were terminalized";
recoverProjections(operation, diagnostic);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Show the query behind findFailedOperationsWithActiveProjections.
fd -t f 'RagIndexOperationRepository.java' --exec cat -n {}

Repository: rostilos/CodeCrow

Length of output: 3395


🏁 Script executed:

#!/bin/bash
set -eu

service=$(fd -t f 'RagIndexOperationRecoveryService.java' | head -n 1)
printf '%s\n' "=== $service ==="
cat -n "$service"

printf '%s\n' '=== related recovery symbols ==='
rg -n -S \
  'recoverProjections|terminalizePrimaryStatus|findFailedOperationsWithActiveProjections|class RagIndexOperationRecoveryService|activeJobId|analysis_lock|RAG_INDEXING' \
  java-ecosystem/libs java-ecosystem 2>/dev/null | head -n 240

Repository: rostilos/CodeCrow

Length of output: 28307


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '=== recovery tests ==='
test_file=$(fd -t f 'RagIndexOperationRecoveryServiceTest.java' | head -n 1)
cat -n "$test_file"

printf '%s\n' '=== job failure implementation ==='
fd -t f 'JobService.java' --exec rg -n -A 45 -B 12 \
  'failJob|isTerminal|class JobService' {}

printf '%s\n' '=== lock release implementation ==='
fd -t f 'AnalysisLockService.java' --exec rg -n -A 60 -B 18 \
  'releaseLock|acquireLock|lockKey|commitHash|expiresAt' {}

printf '%s\n' '=== tracking terminalization implementation ==='
fd -t f 'RagIndexTrackingService.java' --exec rg -n -A 45 -B 12 \
  'markIndexingFailed|markIncrementalUpdateFailed|getIndexStatus|activeJobId' {}

Repository: rostilos/CodeCrow

Length of output: 48061


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '=== analysis lock key assignments ==='
rg -n -A 12 -B 12 \
  'setAnalysisLockKey|getAnalysisLockKey|analysisLockKey|new RagIndexOperation|RagIndexOperation\(' \
  java-ecosystem/libs --glob '*.java' --glob '*.sql' | head -n 260

printf '%s\n' '=== operation model and schema ==='
fd -t f 'RagIndexOperation.java' --exec cat -n {}
rg -n -A 10 -B 8 \
  'CREATE TABLE.*rag_index_operation|analysis_lock_key|to_revision|job_id' \
  java-ecosystem/libs/core/src/main/resources --glob '*.sql' | head -n 220

printf '%s\n' '=== lock query and cleanup predicates ==='
fd -t f 'AnalysisLockRepository.java' --exec rg -n -A 35 -B 12 \
  'deleteByLockKey|existsActiveLock|findByLockKey|commitHash|expiresAt' {}

Repository: rostilos/CodeCrow

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '=== registry lock-key lifecycle ==='
file=$(fd -t f 'RagBranchIndexRegistryService.java' | head -n 1)
rg -n -A 18 -B 18 \
  'registerBuild|startBuild|setAnalysisLockKey|analysisLockKey' "$file"

printf '%s\n' '=== schema definition for analysis_lock ==='
rg -n -A 16 -B 6 \
  'CREATE TABLE.*analysis_lock|expires_at|lock_key|commit_hash' \
  java-ecosystem/libs/core/src/main/resources/db/migration --glob '*.sql' | head -n 140

printf '%s\n' '=== operation schema migration ==='
rg -n -A 20 -B 8 \
  'rag_index_operation|analysis_lock_key' \
  java-ecosystem/libs/core/src/main/resources/db/migration --glob '*.sql' | head -n 160

Repository: rostilos/CodeCrow

Length of output: 35488


🏁 Script executed:

#!/bin/bash
set -eu

rg -n -A 4 -B 4 \
  '\.startBuild\(' \
  java-ecosystem/libs --glob '*.java' | head -n 180

Repository: rostilos/CodeCrow

Length of output: 8342


Make lock recovery converge when the lock owner key is missing.

The job and primary-status paths converge. A startBuild overload can store a null analysisLockKey, so recovery cannot release an existing lock. The lock query also matches expired rows until cleanup runs. Pass the exact key for every lock-backed start and require an unexpired lock in the query.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/branch/RagIndexOperationRecoveryService.java`
around lines 80 - 86, Update lock recovery around recoverProjections and all
lock-backed startBuild overloads so every started operation persists the exact
analysisLockKey, including the overload that currently permits null. Adjust the
lock lookup used during recovery to require the lock’s expiration timestamp to
be in the future, preventing expired rows from matching before cleanup.

Comment on lines +751 to +770
private static int trimBareUrlEnd(String text, int start, int end) {
int trimmedEnd = end;
while (trimmedEnd > start) {
char trailing = text.charAt(trimmedEnd - 1);
if (".,;!?".indexOf(trailing) >= 0) {
trimmedEnd--;
continue;
}
char opening = switch (trailing) {
case ')' -> '(';
case ']' -> '[';
case '}' -> '{';
default -> '\0';
};
if (opening == '\0' || !hasUnmatchedClosingDelimiter(
text, start, trimmedEnd, opening, trailing)) {
break;
}
trimmedEnd--;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Trim trailing double quotes from bare URLs.

If a URL is enclosed in double quotes, trimBareUrlEnd keeps the closing ". The generated href then differs from the URL and includes punctuation.

Trim " with the other trailing punctuation. Add a regression test for "https://example.test/path".

Proposed fix
-            if (".,;!?".indexOf(trailing) >= 0) {
+            if (".,;!?\"".indexOf(trailing) >= 0) {
📝 Committable suggestion

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

Suggested change
private static int trimBareUrlEnd(String text, int start, int end) {
int trimmedEnd = end;
while (trimmedEnd > start) {
char trailing = text.charAt(trimmedEnd - 1);
if (".,;!?".indexOf(trailing) >= 0) {
trimmedEnd--;
continue;
}
char opening = switch (trailing) {
case ')' -> '(';
case ']' -> '[';
case '}' -> '{';
default -> '\0';
};
if (opening == '\0' || !hasUnmatchedClosingDelimiter(
text, start, trimmedEnd, opening, trailing)) {
break;
}
trimmedEnd--;
}
private static int trimBareUrlEnd(String text, int start, int end) {
int trimmedEnd = end;
while (trimmedEnd > start) {
char trailing = text.charAt(trimmedEnd - 1);
if (".,;!?\"".indexOf(trailing) >= 0) {
trimmedEnd--;
continue;
}
char opening = switch (trailing) {
case ')' -> '(';
case ']' -> '[';
case '}' -> '{';
default -> '\0';
};
if (opening == '\0' || !hasUnmatchedClosingDelimiter(
text, start, trimmedEnd, opening, trailing)) {
break;
}
trimmedEnd--;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/libs/task-management/src/main/java/org/rostilos/codecrow/taskmanagement/jira/cloud/JiraCloudClient.java`
around lines 751 - 770, Update trimBareUrlEnd to treat a trailing double quote
as removable punctuation alongside .,;!? so quoted bare URLs produce an href
without the closing quote. Add a regression test covering
"https://example.test/path" and verify the generated URL excludes the quote.

Comment on lines +602 to +611
public static boolean isPublicPreviewOnlyComment(String body) {
if (body == null || (!body.contains("/share#token=ccs_")
&& !body.contains("/share?token=ccs_"))) {
return false;
}
String withoutPreviewLink = body
.replaceAll("https?://\\S+/share(?:#|\\?)token=ccs_[A-Za-z0-9_-]+", "")
.replace("View QA test cases in CodeCrow", "")
.replaceAll("[\\[\\]()\\s]", "");
return withoutPreviewLink.isBlank();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Remove the auto-document marker before classifying a link-only comment.

findCommentByMarker(..., COMMENT_MARKER_PREFIX) selects comments that contain the CodeCrow marker. Lines 607-611 remove the preview URL and label but retain that marker. The method then returns false for a marker plus preview-link-only comment. This causes the fallback to use a public link as previous QA documentation.

Strip COMMENT_MARKER and its PR-tracking variants before the blank-content check. Add a test with <!-- codecrow-qa-autodoc --> before the preview link.

Proposed fix
         String withoutPreviewLink = body
                 .replaceAll("https?://\\S+/share(?:#|\\?)token=ccs_[A-Za-z0-9_-]+", "")
                 .replace("View QA test cases in CodeCrow", "")
+                .replaceAll("<!--\\s*codecrow-qa-autodoc(?::[^>]*)?\\s*-->", "")
                 .replaceAll("[\\[\\]()\\s]", "");
📝 Committable suggestion

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

Suggested change
public static boolean isPublicPreviewOnlyComment(String body) {
if (body == null || (!body.contains("/share#token=ccs_")
&& !body.contains("/share?token=ccs_"))) {
return false;
}
String withoutPreviewLink = body
.replaceAll("https?://\\S+/share(?:#|\\?)token=ccs_[A-Za-z0-9_-]+", "")
.replace("View QA test cases in CodeCrow", "")
.replaceAll("[\\[\\]()\\s]", "");
return withoutPreviewLink.isBlank();
public static boolean isPublicPreviewOnlyComment(String body) {
if (body == null || (!body.contains("/share#token=ccs_")
&& !body.contains("/share?token=ccs_"))) {
return false;
}
String withoutPreviewLink = body
.replaceAll("https?://\\S+/share(?:#|\\?)token=ccs_[A-Za-z0-9_-]+", "")
.replace("View QA test cases in CodeCrow", "")
.replaceAll("<!--\\s*codecrow-qa-autodoc(?::[^>]*)?\\s*-->", "")
.replaceAll("[\\[\\]()\\s]", "");
return withoutPreviewLink.isBlank();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java`
around lines 602 - 611, Update isPublicPreviewOnlyComment to remove
COMMENT_MARKER and its PR-tracking variants along with the preview URL and label
before checking blank content. Preserve classification as true for comments
containing only an auto-document marker and preview link, and add a test
covering <!-- codecrow-qa-autodoc --> before the preview link.

Comment on lines +39 to +51
public String buildTaskComment(String qaDocument, String previewUrl) {
if (previewUrl == null || previewUrl.isBlank()) {
throw new IllegalArgumentException("A public preview URL is required.");
}
String normalizedPreviewUrl = previewUrl.trim();
return QaAutoDocListener.COMMENT_MARKER
+ "\n\n"
+ QaDocContentParser.replaceShareableSections(
qaDocument,
withTab(normalizedPreviewUrl, "test-cases"),
withTab(normalizedPreviewUrl, "environment")
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check which markdown the callers pass to createPreviewUrl and buildTaskComment.
rg -n -C 8 'createPreviewUrl|buildTaskComment' --type=java -g '!**/test/**'

Repository: rostilos/CodeCrow

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -eu
file='java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java'
printf '%s\n' '--- target file ---'
sed -n '1,140p' "$file"
printf '%s\n' '--- related Java files ---'
rg -l --type=java 'QaDocPublicPreviewService|createPreviewUrl|buildTaskComment' . || true
printf '%s\n' '--- method references ---'
rg -n -C 12 --type=java 'createPreviewUrl|buildTaskComment|QaDocPublicPreviewService' . || true

Repository: rostilos/CodeCrow

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- production call sites ---'
rg -n -C 18 --type=java '\.buildTaskComment\(|\.createPreviewUrl\(' \
  java-ecosystem/services/pipeline-agent/src/main/java
printf '%s\n' '--- parser declarations and implementation ---'
rg -n -C 20 --type=java 'replaceShareableSections|parseMarkedTestCases' \
  java-ecosystem
printf '%s\n' '--- relevant listener and processor sections ---'
sed -n '180,330p' java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java
sed -n '1,260p' java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java

Repository: rostilos/CodeCrow

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
parser=$(rg -l --type=java 'class QaDocContentParser' java-ecosystem)
printf '%s\n' "--- parser: $parser ---"
rg -n -A 80 -B 10 'replaceShareableSections|parseMarkedTestCases' "$parser"
printf '%s\n' '--- listener persistence helper ---'
rg -n -A 90 -B 15 'upsertQaDocDocument|pendingHandoffDocument|qaDocument' \
  java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java
printf '%s\n' '--- command processor persistence helper ---'
rg -n -A 90 -B 15 'upsertQaDocDocument|pendingHandoffDocument|qaDocument' \
  java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java

Repository: rostilos/CodeCrow

Length of output: 878


🏁 Script executed:

#!/bin/bash
set -eu
parser='java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java'
printf '%s\n' "--- parser: $parser ---"
rg -n -A 90 -B 10 'replaceShareableSections|parseMarkedTestCases' "$parser"
printf '%s\n' '--- listener persistence references ---'
rg -n -A 55 -B 12 'upsertQaDocDocument|pendingHandoffDocument' \
  java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java
printf '%s\n' '--- command processor persistence references ---'
rg -n -A 55 -B 12 'upsertQaDocDocument|pendingHandoffDocument' \
  java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/processor/command/QaDocCommandProcessor.java

Repository: rostilos/CodeCrow

Length of output: 32385


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path

service = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java"
).read_text()
listener = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java"
).read_text()
processor = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/generic/processor/command/"
    "QaDocCommandProcessor.java"
).read_text()
parser = Path(
    "java-ecosystem/libs/core/src/main/java/"
    "org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java"
).read_text()

assert service.index("public String createPreviewUrl") < service.index("public String buildTaskComment")
assert service.index("publicShares.issue") < service.index("buildTaskComment")
assert "createPreviewUrl(persistedDocument.get())" in listener
assert "buildTaskComment(qaDocument, previewUrl)" in listener
assert "createPreviewUrl(persistedDocument.get())" in processor
assert "buildTaskComment(qaDocument, previewUrl)" in processor
assert 'if (section == null)' in parser
assert 'throw new IllegalArgumentException("A marked QA test-case section is required.")' in parser

print("production callers use persistedDocument for URL validation and qaDocument for comment rendering")
print("token issuance precedes comment rendering")
print("comment rendering rejects input without a marked test-case section")
PY

Repository: rostilos/CodeCrow

Length of output: 261


🏁 Script executed:

#!/bin/bash
python3 - <<'PY'
from pathlib import Path

service = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java"
).read_text()
listener = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/qadoc/QaAutoDocListener.java"
).read_text()
processor = Path(
    "java-ecosystem/services/pipeline-agent/src/main/java/"
    "org/rostilos/codecrow/pipelineagent/generic/processor/command/"
    "QaDocCommandProcessor.java"
).read_text()
parser = Path(
    "java-ecosystem/libs/core/src/main/java/"
    "org/rostilos/codecrow/core/service/qadoc/QaDocContentParser.java"
).read_text()

assert service.index("public String createPreviewUrl") < service.index("public String buildTaskComment")
assert service.index("publicShareLinkService.issue") < service.index("buildTaskComment")
assert "createPreviewUrl(persistedDocument.get())" in listener
assert "buildTaskComment(qaDocument, previewUrl)" in listener
assert "createPreviewUrl(persistedDocument.get())" in processor
assert "buildTaskComment(qaDocument, previewUrl)" in processor
assert 'if (section == null)' in parser
assert 'throw new IllegalArgumentException("A marked QA test-case section is required.")' in parser

print("production callers use persistedDocument for URL validation and qaDocument for comment rendering")
print("token issuance precedes comment rendering")
print("comment rendering rejects input without a marked test-case section")
PY

Repository: rostilos/CodeCrow

Length of output: 361


Use the same QaDocDocument for preview validation and comment rendering.

The callers pass persistedDocument to createPreviewUrl but pass qaDocument to buildTaskComment. If qaDocument lacks the marked test-case section, rendering throws after PublicShareLinkService.issue creates the token. Accept QaDocDocument in buildTaskComment, or combine both operations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java`
around lines 39 - 51, The preview flow currently validates one document but
renders another, allowing a token to be issued before rendering fails. Update
buildTaskComment to accept and render the same QaDocDocument instance passed to
createPreviewUrl, and adjust its callers so persistedDocument is used
consistently for both operations.

Comment on lines +135 to +139
private boolean hasCompatibleTaskKey(CodeAnalysis analysis, String documentTaskKey) {
String analysisTaskKey = normalize(analysis.getTaskId());
return documentTaskKey == null || analysisTaskKey == null
|| Objects.equals(documentTaskKey, analysisTaskKey);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Require matching task keys before exposing the task summary.

When either task key is null, this method returns true. findTaskSummary then includes the analysis summary in the public preview. A missing task key cannot prove that the analysis belongs to the same task.

Return false unless both normalized keys are present and equal. Add a regression test for a missing document or analysis task key.

Proposed fix
 private boolean hasCompatibleTaskKey(CodeAnalysis analysis, String documentTaskKey) {
     String analysisTaskKey = normalize(analysis.getTaskId());
-    return documentTaskKey == null || analysisTaskKey == null
-            || Objects.equals(documentTaskKey, analysisTaskKey);
+    return documentTaskKey != null
+            && Objects.equals(documentTaskKey, analysisTaskKey);
 }
📝 Committable suggestion

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

Suggested change
private boolean hasCompatibleTaskKey(CodeAnalysis analysis, String documentTaskKey) {
String analysisTaskKey = normalize(analysis.getTaskId());
return documentTaskKey == null || analysisTaskKey == null
|| Objects.equals(documentTaskKey, analysisTaskKey);
}
private boolean hasCompatibleTaskKey(CodeAnalysis analysis, String documentTaskKey) {
String analysisTaskKey = normalize(analysis.getTaskId());
return documentTaskKey != null
&& Objects.equals(documentTaskKey, analysisTaskKey);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProvider.java`
around lines 135 - 139, Update hasCompatibleTaskKey to return true only when
both the normalized analysis task key and documentTaskKey are non-null and
equal; return false for either missing key. Add a regression test covering a
missing document task key or analysis task key and verify the task summary is
not exposed.

Comment on lines +726 to +775
async def _ensure_test_cases(
self,
documentation: str,
placeholders: Dict[str, str],
) -> str:
"""Guarantee an independently extractable test-case section.

Normal generation is instructed to emit stable invisible markers. If a
custom template or model response omits them, one focused repair call
generates only the missing section without narrowing the requested test
coverage.
"""
if self._contains_extractable_test_cases(documentation):
return self._normalize_test_case_markers(documentation)

repair_placeholders = dict(placeholders)
raw_diff = repair_placeholders.get("diff", "")
max_repair_diff = 120_000
if len(raw_diff) > max_repair_diff:
repair_placeholders["diff"] = (
raw_diff[:max_repair_diff]
+ f"\n\n... (diff truncated — {len(raw_diff)} chars total, "
f"showing first {max_repair_diff})"
)

logger.warning("QA doc omitted extractable test cases; running focused repair generation")
prompt = QA_DOC_TEST_CASES_REPAIR_PROMPT.format(**repair_placeholders)
response = await self.llm.ainvoke([
{"role": "system", "content": QA_DOC_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
])
generated = self._extract_text(response).strip()
if not generated:
raise ValueError("Test-case generation returned no content")

start_marker = "<!-- codecrow-test-cases:start -->"
end_marker = "<!-- codecrow-test-cases:end -->"
start = generated.find(start_marker)
end = generated.find(end_marker, start + len(start_marker)) if start >= 0 else -1
if start >= 0 and end >= 0:
test_case_section = generated[start:end + len(end_marker)]
else:
test_case_section = f"{start_marker}\n{generated}\n{end_marker}"

if not self._contains_extractable_test_cases(test_case_section):
raise ValueError("Test-case generation returned no structured scenarios")

return self._normalize_test_case_markers(
documentation.rstrip() + "\n\n" + test_case_section.strip()
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reject unmarked repair output.

If repair generation omits the markers, Lines 767-768 wrap the complete model response in the public test-case boundary. The response can contain preambles or non-test-case technical content. Require one valid marked section, or fail the repair.

Proposed fix
         if start >= 0 and end >= 0:
             test_case_section = generated[start:end + len(end_marker)]
         else:
-            test_case_section = f"{start_marker}\n{generated}\n{end_marker}"
+            raise ValueError("Test-case generation omitted required markers")
📝 Committable suggestion

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

Suggested change
async def _ensure_test_cases(
self,
documentation: str,
placeholders: Dict[str, str],
) -> str:
"""Guarantee an independently extractable test-case section.
Normal generation is instructed to emit stable invisible markers. If a
custom template or model response omits them, one focused repair call
generates only the missing section without narrowing the requested test
coverage.
"""
if self._contains_extractable_test_cases(documentation):
return self._normalize_test_case_markers(documentation)
repair_placeholders = dict(placeholders)
raw_diff = repair_placeholders.get("diff", "")
max_repair_diff = 120_000
if len(raw_diff) > max_repair_diff:
repair_placeholders["diff"] = (
raw_diff[:max_repair_diff]
+ f"\n\n... (diff truncated — {len(raw_diff)} chars total, "
f"showing first {max_repair_diff})"
)
logger.warning("QA doc omitted extractable test cases; running focused repair generation")
prompt = QA_DOC_TEST_CASES_REPAIR_PROMPT.format(**repair_placeholders)
response = await self.llm.ainvoke([
{"role": "system", "content": QA_DOC_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
])
generated = self._extract_text(response).strip()
if not generated:
raise ValueError("Test-case generation returned no content")
start_marker = "<!-- codecrow-test-cases:start -->"
end_marker = "<!-- codecrow-test-cases:end -->"
start = generated.find(start_marker)
end = generated.find(end_marker, start + len(start_marker)) if start >= 0 else -1
if start >= 0 and end >= 0:
test_case_section = generated[start:end + len(end_marker)]
else:
test_case_section = f"{start_marker}\n{generated}\n{end_marker}"
if not self._contains_extractable_test_cases(test_case_section):
raise ValueError("Test-case generation returned no structured scenarios")
return self._normalize_test_case_markers(
documentation.rstrip() + "\n\n" + test_case_section.strip()
)
async def _ensure_test_cases(
self,
documentation: str,
placeholders: Dict[str, str],
) -> str:
"""Guarantee an independently extractable test-case section.
Normal generation is instructed to emit stable invisible markers. If a
custom template or model response omits them, one focused repair call
generates only the missing section without narrowing the requested test
coverage.
"""
if self._contains_extractable_test_cases(documentation):
return self._normalize_test_case_markers(documentation)
repair_placeholders = dict(placeholders)
raw_diff = repair_placeholders.get("diff", "")
max_repair_diff = 120_000
if len(raw_diff) > max_repair_diff:
repair_placeholders["diff"] = (
raw_diff[:max_repair_diff]
f"\n\n... (diff truncated — {len(raw_diff)} chars total, "
f"showing first {max_repair_diff})"
)
logger.warning("QA doc omitted extractable test cases; running focused repair generation")
prompt = QA_DOC_TEST_CASES_REPAIR_PROMPT.format(**repair_placeholders)
response = await self.llm.ainvoke([
{"role": "system", "content": QA_DOC_SYSTEM_PROMPT},
{"role": "user", "content": prompt},
])
generated = self._extract_text(response).strip()
if not generated:
raise ValueError("Test-case generation returned no content")
start_marker = "<!-- codecrow-test-cases:start -->"
end_marker = "<!-- codecrow-test-cases:end -->"
start = generated.find(start_marker)
end = generated.find(end_marker, start + len(start_marker)) if start >= 0 else -1
if start >= 0 and end >= 0:
test_case_section = generated[start:end + len(end_marker)]
else:
raise ValueError("Test-case generation omitted required markers")
if not self._contains_extractable_test_cases(test_case_section):
raise ValueError("Test-case generation returned no structured scenarios")
return self._normalize_test_case_markers(
documentation.rstrip() + "\n\n" + test_case_section.strip()
)
🧰 Tools
🪛 ast-grep (0.45.1)

[warning] 762-762: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: generated.find(start_marker)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').

(xpath-injection-python)


[warning] 763-763: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: generated.find(end_marker, start + len(start_marker))
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').

(xpath-injection-python)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@python-ecosystem/inference-orchestrator/src/service/qa_documentation/qa_doc_orchestrator.py`
around lines 726 - 775, Update _ensure_test_cases to reject repair responses
that omit the codecrow test-case markers instead of wrapping the entire
generated response in a fabricated section. Require both valid start and end
markers, extract only that marked section, and raise the existing repair failure
error when either marker is missing; preserve the structured-scenario validation
for marked output.

rostilos and others added 2 commits August 12, 2026 21:05
- renew and fence PR, branch, exact-generation, and legacy RAG leases
- recover abandoned jobs without corrupting newer owners or checkpoints
- fix detached RAG generation proxies by using scalar projections
- make exact generation admission, publication, and status recovery durable
- preserve legacy Qdrant collections and repair payload indexes in place
- harden PR overlay cleanup, alias reconciliation, and transient cleanup
- handle excluded incremental files without failing the indexing job
- treat lock contention as a skipped update instead of a failure
- drain admitted queue and streaming work during graceful shutdown
- serialize terminal events and add bounded Redis event retention
- degrade optional RAG context without failing the core review
- remove unused QA documentation RAG overlays and credential forwarding
- suppress duplicate diagnostics while retaining owner-level failures
- add recovery configuration samples, migration, and regression coverage
fix(reliability): stabilize analysis queues and RAG lifecycle
rostilos and others added 2 commits August 13, 2026 00:08
- prevent PR reviews from synchronously starting or waiting for repository RAG builds
- clarify and rate-limit cross-type PR/branch dependency wait events
- keep unrelated PRs and branches independently executable
- harden analysis locks, leases, ownership fencing, and durable job recovery
- fix detached RAG generation proxy failures during incremental updates
- build exact generations from complete revision-pinned repository snapshots
- reuse compatible vectors from the previous immutable generation
- make RAG cleanup, alias reconciliation, and queue shutdown reliable
- bound repeated outage diagnostics and remove duplicate terminal events
- preserve compatibility with existing Qdrant collections and configuration
- add regression coverage across Pipeline Agent, RAG, and Inference services
fix: stabilize analysis orchestration and RAG generation lifecycle
@codecrow-local

codecrow-local Bot commented Aug 12, 2026

Copy link
Copy Markdown

⚠️ Code Analysis Results

Quality Gate Default Quality Gate: 🔴 FAILED

  • MEDIUM Issues by Severity > 0 (actual: 6) - FAILED

Summary

Pull Request Review: 1.8.1 rc

Status PASS WITH WARNINGS
Risk Level MEDIUM
Review Coverage 17 files analyzed in depth
Confidence MEDIUM

Executive Summary

This release-candidate PR updates RAG index generation and collection targeting, analysis gating and wait handling, job/index lifecycle behavior, and QA document processing. The reviewed changes show no critical or high-severity blockers, but several medium-risk correctness, compatibility, error-handling, and test-alignment concerns remain across these areas. No task context was provided, so task-coverage confidence is based solely on the reviewed PR changes.

Recommendation

Decision: PASS WITH WARNINGS

The PR may proceed if the identified warnings are addressed or explicitly accepted, with particular attention to backward compatibility for existing index state, recovery behavior, parser correctness, and failing or mismatched test expectations. Further validation of affected integration paths is recommended before release.

Issues Overview

Severity Count
🟡 Medium 6 Issues that should be addressed
🔵 Low 2 Minor issues and improvements
✅ Resolved 1 Resolved issues

Analysis completed on 2026-08-12 21:13:40 | View Full Report | Pull Request


📋 Detailed Issues (8)

🟡 Medium Severity Issues

Id on Platform: 4054

Category: 🧪 Testing

File: .../branch/BranchAnalysisGateServiceTest.java:115

Assertions expect fields absent from wait events

The updated matcher requires waitingJobType and blockingJobType to be present in the emitted event. However, the current BranchAnalysisGateService.emitWait and emitBranchWait implementations populate the event with type, state, message, branch name, waited time, and optionally PR number, but do not add either of these fields. Consequently, mergeWaitsOnlyForNewestAttemptOfItsOwnPr and the corresponding branch-wait test will fail whenever a wait event is emitted.

💡 Suggested Fix

Either remove the two unsupported fields from both test matchers, or update the production event builders to emit them consistently and retain the assertions. The test should reflect the actual event contract intended by the service.

View Issue Details


Id on Platform: 4055

Category: 🐛 Bug Risk

File: .../service/RagIndexTrackingService.java:75

Legacy transitions silently fail under active ownership

The compatibility overload delegates markIndexingCompleted with expectedActiveJobId set to null, while ownsStatus requires the expected owner to equal the status row's active job ID. Once a status is owned by any non-null job, this overload returns the unchanged status without completing the index. The same delegation pattern exists for the retained overloads of failure, heartbeat, and incremental completion. Any caller still using these public legacy signatures can therefore report successful work while leaving the status stuck in INDEXING or UPDATING.

Also affects: java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java:74

💡 Suggested Fix

Preserve a deliberate compatibility contract for the legacy overloads: either migrate/remove all legacy entry points, or make their behavior explicit rather than passing null through the stale-worker ownership check. If legacy calls are allowed to transition state, add a separate transition path that documents and enforces the appropriate ownership policy.

View Issue Details


Id on Platform: 4058

Category: 🐛 Bug Risk

File: .../index_manager/collection_manager.py:144

Mismatched payload schemas are never repaired

The required schema now changes fields such as pr from the previous all-KEYWORD configuration to BOOL, but _ensure_payload_indexes only skips an index when its existing type exactly matches and otherwise calls create_payload_index again. Qdrant does not generally replace an existing payload index when a different schema is requested; it returns a conflict/error instead. As a result, collections created before this change can retain incompatible schemas, the repair is retried on every use, and filters on those fields may remain unsupported or fail.

💡 Suggested Fix

Detect existing indexes with an incompatible schema and explicitly delete/recreate them using the required type, or provide a one-time migration path. Do not mark the collection repaired until all mismatched indexes have been successfully migrated.

View Issue Details


Id on Platform: 4059

Category: 🐛 Bug Risk

File: .../qadoc/QaDocContentParser.java:30

Any section six is parsed as environment

The first alternative in ENVIRONMENT_SECTION_HEADING matches any heading beginning with 6., regardless of its title. A document containing a legitimate section such as ## 6. Security Considerations or ## 6. Deployment Risks will have that section removed from overview and exposed as environment instead. This can corrupt parsed QA document content and shareable-section replacement behavior for otherwise valid numbered documents.

💡 Suggested Fix

Remove the broad 6.\s+.+ alternative and match only the supported environment/setup titles. If a numbered legacy environment heading is required, constrain the title after the number to the known environment wording.

View Issue Details


Id on Platform: 4060

Category: 🐛 Bug Risk

File: .../analysis/RagIndexStatusRepository.java:55

Recovery marks missing indexes as indexed

recoverAbandonedIncrementalUpdate unconditionally changes an abandoned INDEXING or UPDATING row to INDEXED, but its predicate does not require lastIndexedAt to be non-null. For a first indexing attempt with no completed checkpoint, recovery therefore advertises an index as usable even though no prior index exists. Consumers using isProjectIndexed can then skip required index creation or query a nonexistent collection.

💡 Suggested Fix

Restrict the restore-to-INDEXED update to rows with a usable prior checkpoint, such as r.lastIndexedAt IS NOT NULL. Handle an abandoned first indexing attempt separately by transitioning it to an appropriate failed/unindexed state.

View Issue Details


Id on Platform: 4061

Category: 🐛 Bug Risk

File: .../qadoc/QaDocContentParser.java:30

QA content parser misclassifies arbitrary numbered section 6 and corrupts downstream previews

QA content parser misclassifies arbitrary numbered section 6 and corrupts downstream previews
QaDocContentParser treats every heading beginning with 6. as the environment/setup section, regardless of its title. The parser is used both when generating the public-preview content and when resolving that content for anonymous sharing. A valid document containing a section such as ## 6. Security Considerations is therefore split incorrectly: the section is removed from the overview and returned as environment/setup content instead.
Evidence: QaDocPublicPreviewService parses the persisted QA document before issuing a share, while QaDocShareProvider parses the same document when constructing the public response. Both consumers consequently inherit the parser's broad 6. match and can expose or render the wrong sections in the preview.
Business impact: QA guides with legitimate numbered section 6 content can lose that content from the overview and display it under the wrong public-preview section, producing misleading documentation and potentially exposing setup-like content in an unintended response field.
Also affects: java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java, java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProvider.java

💡 Suggested Fix

Restrict the numbered-section alternative to the intended environment/setup heading, or require the heading text to match the known environment/setup titles. Add an end-to-end test covering a document with ## 6. Security Considerations through both preview generation and public-share resolution.

View Issue Details


🔵 Low Severity Issues

Id on Platform: 4056

Category: 🐛 Bug Risk

File: .../cloud/JiraCloudClient.java:671

URLs inside code spans become links

The bare-URL branch runs before the existing inline-code branch. Consequently, Markdown such as `https://example.com` is split into a linked URL node instead of remaining code-formatted text. This changes the documented inline-code behavior and can alter literal command/configuration text when Jira comments are generated.

💡 Suggested Fix

Handle inline-code spans before bare URL detection, or explicitly suppress URL recognition while the parser is inside a code span. Preserve the existing code mark for the complete span.

View Issue Details


Id on Platform: 4057

Category: 🛡️ Error Handling

File: .../service/AnalysisJobService.java:96

Null skip reason causes exception

Map.of rejects null keys and values. The public skipJob method accepts an unconstrained String reason, so a caller passing null causes an immediate NullPointerException instead of completing the job as skipped. This makes the new lifecycle helper unsafe for optional or unavailable diagnostic reasons.

💡 Suggested Fix

Use a null-tolerant mutable map, normalize a null reason to an empty/default message, or explicitly validate and reject null with a clear IllegalArgumentException before constructing the result map.

View Issue Details


Files Affected

  • .../qadoc/QaDocContentParser.java: 2 issues
  • .../service/RagIndexTrackingService.java: 1 issue
  • .../index_manager/collection_manager.py: 1 issue
  • .../cloud/JiraCloudClient.java: 1 issue
  • .../analysis/RagIndexStatusRepository.java: 1 issue
  • .../branch/BranchAnalysisGateServiceTest.java: 1 issue
  • .../service/AnalysisJobService.java: 1 issue

@codecrow-local codecrow-local Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CodeCrow Review

Actionable comments posted: 8

Each finding below is attached to the relevant changed line. The complete analysis remains available in the CodeCrow summary comment.

event -> Long.valueOf(41L).equals(event.get("prNumber"))
&& event.get("message").toString().contains("PR #41")));
&& event.get("message").toString().contains("PR #41")
&& JobType.BRANCH_ANALYSIS.name().equals(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Testing

Assertions expect fields absent from wait events

The updated matcher requires waitingJobType and blockingJobType to be present in the emitted event. However, the current BranchAnalysisGateService.emitWait and emitBranchWait implementations populate the event with type, state, message, branch name, waited time, and optionally PR number, but do not add either of these fields. Consequently, mergeWaitsOnlyForNewestAttemptOfItsOwnPr and the corresponding branch-wait test will fail whenever a wait event is emitted.

💡 Suggested fix

Either remove the two unsupported fields from both test matchers, or update the production event builders to emit them consistently and retain the assertions. The test should reflect the actual event contract intended by the service.

View issue in CodeCrow

Integer filesIndexed, Integer chunkCount) {
RagIndexStatus status = ragIndexStatusRepository.findByProjectId(project.getId())
return markIndexingCompleted(
project, branchName, commitHash, filesIndexed, chunkCount, null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Bug Risk

Legacy transitions silently fail under active ownership

The compatibility overload delegates markIndexingCompleted with expectedActiveJobId set to null, while ownsStatus requires the expected owner to equal the status row's active job ID. Once a status is owned by any non-null job, this overload returns the unchanged status without completing the index. The same delegation pattern exists for the retained overloads of failure, heartbeat, and incremental completion. Any caller still using these public legacy signatures can therefore report successful work while leaving the status stuck in INDEXING or UPDATING.

Also affects: java-ecosystem/libs/rag-engine/src/main/java/org/rostilos/codecrow/ragengine/service/RagIndexTrackingService.java:74

💡 Suggested fix

Preserve a deliberate compatibility contract for the legacy overloads: either migrate/remove all legacy entry points, or make their behavior explicit rather than passing null through the stale-worker ownership check. If legacy calls are allowed to transition state, add a separate transition path that documents and enforces the appropriate ownership policy.

View issue in CodeCrow

// Jira does not auto-link plain text inside an ADF document. Add
// the link mark explicitly, preserving fragments used by opaque
// public-share URLs.
if (text.startsWith("https://", pos) || text.startsWith("http://", pos)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 LOW | Bug Risk

URLs inside code spans become links

The bare-URL branch runs before the existing inline-code branch. Consequently, Markdown such as `https://example.com` is split into a linked URL node instead of remaining code-formatted text. This changes the documented inline-code behavior and can alter literal command/configuration text when Jira comments are generated.

💡 Suggested fix

Handle inline-code spans before bare URL detection, or explicitly suppress URL recognition while the parser is inside a code span. Preserve the existing code mark for the complete span.

View issue in CodeCrow


/** Finish an intentionally skipped job without representing it as a failure. */
default void skipJob(Job job, String reason) {
completeJob(job, Map.of("status", "skipped", "reason", reason));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔵 LOW | Error Handling

Null skip reason causes exception

Map.of rejects null keys and values. The public skipJob method accepts an unconstrained String reason, so a caller passing null causes an immediate NullPointerException instead of completing the job as skipped. This makes the new lifecycle helper unsafe for optional or unavailable diagnostic reasons.

💡 Suggested fix

Use a null-tolerant mutable map, normalize a null reason to an empty/default message, or explicitly validate and reject null with a clear IllegalArgumentException before constructing the result map.

View issue in CodeCrow

("architecture_group", PayloadSchemaType.KEYWORD),
("snapshot_plugin", PayloadSchemaType.KEYWORD),
("snapshot_kind", PayloadSchemaType.KEYWORD),
("pr", PayloadSchemaType.BOOL),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Bug Risk

Mismatched payload schemas are never repaired

The required schema now changes fields such as pr from the previous all-KEYWORD configuration to BOOL, but _ensure_payload_indexes only skips an index when its existing type exactly matches and otherwise calls create_payload_index again. Qdrant does not generally replace an existing payload index when a different schema is requested; it returns a conflict/error instead. As a result, collections created before this change can retain incompatible schemas, the repair is retried on every use, and filters on those fields may remain unsupported or fail.

💡 Suggested fix

Detect existing indexes with an incompatible schema and explicitly delete/recreate them using the required type, or provide a one-time migration path. Do not mark the collection repaired until all mismatched indexes have been successfully migrated.

View issue in CodeCrow

Pattern.CASE_INSENSITIVE
);
private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
"^(?:6\\.\\s+.+|(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Bug Risk

Any section six is parsed as environment

The first alternative in ENVIRONMENT_SECTION_HEADING matches any heading beginning with 6., regardless of its title. A document containing a legitimate section such as ## 6. Security Considerations or ## 6. Deployment Risks will have that section removed from overview and exposed as environment instead. This can corrupt parsed QA document content and shareable-section replacement behavior for otherwise valid numbered documents.

💡 Suggested fix

Remove the broad 6.\s+.+ alternative and match only the supported environment/setup titles. If a numbered legacy environment heading is required, constrain the title after the number to the known environment wording.

View issue in CodeCrow

"r.errorMessage = CONCAT('Incremental update failed: ', :errorMessage), " +
"r.failedIncrementalCount = COALESCE(r.failedIncrementalCount, 0) + 1, " +
"r.activeJobId = NULL " +
"WHERE r.project.id = :projectId AND r.activeJobId = :jobId " +

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Bug Risk

Recovery marks missing indexes as indexed

recoverAbandonedIncrementalUpdate unconditionally changes an abandoned INDEXING or UPDATING row to INDEXED, but its predicate does not require lastIndexedAt to be non-null. For a first indexing attempt with no completed checkpoint, recovery therefore advertises an index as usable even though no prior index exists. Consumers using isProjectIndexed can then skip required index creation or query a nonexistent collection.

💡 Suggested fix

Restrict the restore-to-INDEXED update to rows with a usable prior checkpoint, such as r.lastIndexedAt IS NOT NULL. Handle an abandoned first indexing attempt separately by transitioning it to an appropriate failed/unindexed state.

View issue in CodeCrow

Pattern.CASE_INSENSITIVE
);
private static final Pattern ENVIRONMENT_SECTION_HEADING = Pattern.compile(
"^(?:6\\.\\s+.+|(?:\\d+\\.\\s*)?(?:Environment and Setup Notes|"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 MEDIUM | Bug Risk

QA content parser misclassifies arbitrary numbered section 6 and corrupts downstream previews

QA content parser misclassifies arbitrary numbered section 6 and corrupts downstream previews
QaDocContentParser treats every heading beginning with 6. as the environment/setup section, regardless of its title. The parser is used both when generating the public-preview content and when resolving that content for anonymous sharing. A valid document containing a section such as ## 6. Security Considerations is therefore split incorrectly: the section is removed from the overview and returned as environment/setup content instead.
Evidence: QaDocPublicPreviewService parses the persisted QA document before issuing a share, while QaDocShareProvider parses the same document when constructing the public response. Both consumers consequently inherit the parser's broad 6. match and can expose or render the wrong sections in the preview.
Business impact: QA guides with legitimate numbered section 6 content can lose that content from the overview and display it under the wrong public-preview section, producing misleading documentation and potentially exposing setup-like content in an unintended response field.
Also affects: java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/qadoc/QaDocPublicPreviewService.java, java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/publicshare/qadoc/QaDocShareProvider.java

💡 Suggested fix

Restrict the numbered-section alternative to the intended environment/setup heading, or require the heading text to match the known environment/setup titles. Add an end-to-end test covering a document with ## 6. Security Considerations through both preview generation and public-share resolution.

View issue in CodeCrow

@rostilos
rostilos merged commit cfa5049 into main Aug 12, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant