Skip to content

Conversation

@youngchingjui
Copy link
Owner

@youngchingjui youngchingjui commented Dec 15, 2025

🚧 DRAFT - This PR has been split into smaller, manageable PRs

⚠️ DO NOT MERGE THIS PR DIRECTLY ⚠️

This large PR (1212 additions, 41 deletions) has been broken down into smaller, focused PRs for easier review and safer merging.

🔄 Split Status

✅ Completed Smaller PRs

  1. PR Improve test infrastructure and Jest configuration #1437 - Test Infrastructure & Configuration ✅ MERGED

    • Fixed Jest moduleNameMapper conflicts and test infrastructure issues
  2. PR Add container security enhancements to prevent command injection #1438 - Container Security Enhancements ⏳ Ready for Review

    • CRITICAL: Fixes command injection vulnerability in execInContainerWithDockerode
    • Enhanced function to support safe array commands
    • Fixed Branch tool vulnerability as example
    • Priority: Should be reviewed/merged ASAP due to security impact
  3. PR Add webhook handler for PR label 'I2PR: Update PR' #1440 - Webhook Handler Foundation ⏳ Ready for Review

    • Added PR label webhook handler for "I2PR: Update PR"
    • Extended PullRequestPayloadSchema with required fields
    • Full test coverage for webhook integration

🔄 Remaining PRs (To Be Created)

  1. Worker Infrastructure - Coming Next

    • Redis/Neo4j connection management
    • Worker orchestrator setup
    • Graceful shutdown handling improvements
  2. CreateDependentPR Core Workflow - Final

    • Main business logic workflow
    • Git operations with error handling
    • Push validation and cleanup safety

🔒 Security Issue Created

Issue #1439 - Complete Migration from String to Array Commands

  • Tracks full security migration across 26+ affected files
  • Complete context and traceability of how the vulnerability was discovered
  • Migration plan and acceptance criteria

📋 Instructions for Undrafting

This PR should only be undrafted and merged when:

  1. PR Improve test infrastructure and Jest configuration #1437 is merged (DONE)
  2. ⏳ PR Add container security enhancements to prevent command injection #1438 is merged (Security fixes - HIGH PRIORITY)
  3. ⏳ PR Add webhook handler for PR label 'I2PR: Update PR' #1440 is merged (Webhook foundation)
  4. ⏳ PR [TBD] is merged (Worker infrastructure)
  5. ⏳ PR [TBD] is merged (Core workflow)
  6. ⏳ All tests pass and functionality is verified

Process:

  1. Review and merge the smaller PRs in order
  2. Once all smaller PRs are merged, this PR can be updated to reflect any remaining changes
  3. Convert from draft to ready for review
  4. Final review and merge

🎯 Why This Approach?

Original Problem: 1212 line PR touching multiple systems - too large to review safely

Benefits of Split Approach:

  • Reviewable chunks - Each PR has clear scope and purpose
  • Independent testing - Smaller changes can be tested thoroughly
  • Security prioritization - Critical vulnerabilities addressed first
  • Reduced risk - Incremental changes vs big-bang deployment
  • Clear traceability - Full context preserved for each component

🔗 Related Links


Original Description (For Reference)

Click to view original PR description

Summary

This follow-up addresses specific review feedback on PR #1403 without changing overall intent.

What I changed

  • Safer container exec (injection mitigation):

    • shared/src/lib/docker.ts: execInContainerWithDockerode now accepts either a string or a string[] (argv). When an array is provided, we pass it directly to Docker, avoiding sh -c and shell interpolation risks.
    • shared/src/usecases/workflows/createDependentPR.ts: converted all user-controlled or ref-dependent git commands to argv form (e.g., ["git", "fetch", "origin", headRef]).
  • Push validation and cleanup safety:

    • shared/src/usecases/workflows/createDependentPR.ts:
      • Do not ignore git push failures. We check the exit code and throw if push fails, preventing a false "completed" workflow state.
      • Wrap containerCleanup() in try/catch so cleanup errors don't mask the original error. A warning status is emitted instead.
  • Octokit installationId type fix:

    • apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts: pass installationId as a number (Number(githubInstallationId)) to satisfy @octokit/auth-app expectations.
  • Tests and config:

    • tests/apps/workers/handler.autoResolveIssue.test.ts: add missing Job type import and adjust messages to include role to satisfy ChatCompletionMessageParam typing.
    • tests/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts: add a test ensuring handler throws when REDIS_URL is missing.
    • tests/config/jest.config.base.ts: remove duplicate moduleNameMapper entry for "^shared/(.*)$".

Notable decisions

Verification

  • Type-only adjustments were done in tests to satisfy typings.
  • Repository-wide tsc has pre-existing path alias errors unrelated to this PR; changes in this follow-up are type-safe locally.

Request

Please review the targeted changes above. Once #1409 merges, we can follow up if any auth scoping updates are still needed here.

Closes #1399


🤖 This PR description was updated to reflect the split approach. Original functionality will be preserved across the smaller PRs.

Summary by CodeRabbit

  • New Features

    • Added automated workflow to create dependent pull requests when a label is applied.
    • Introduced new agent for AI-powered pull request generation with enhanced tooling.
    • Implemented container-based git operations for improved workflow execution.
  • Infrastructure

    • Enhanced worker infrastructure with Neo4j and Redis integration.
    • Improved graceful shutdown handling for multiple data sources.
  • Chores

    • Updated module path configurations for better code organization.
    • Refactored import paths across modules.

✏️ Tip: You can customize this high-level summary in your review settings.

Issue To PR agent and others added 16 commits December 12, 2025 02:22
…Add handler for PR label "I2PR: Update PR" to enqueue a createDependentPR job\n- Extend pull_request webhook payload schema to include label, sender and number\n- Wire new handler into webhook route\n- Define CreateDependentPR job schema in shared package\n- Handle createDependentPR job in worker (stub for now)
…follow-ups\n\n- Worker: add createDependentPR orchestrator and wire it into handler; remove stub and execute shared workflow\n- Shared: migrate DependentPRAgent and createDependentPR workflow to shared so workers can run it\n- Webhook: simplify PR labeled handling by removing redundant installationId missing check (schema guarantees it)\n- Tests: add unit tests for PR label handler enqueuing job and worker routing to orchestrator\n\nKeeps changes scoped to requested feature; no broader refactors.
## Summary
- add a Jest test covering the GitHub webhook issues label path for
auto-resolve
- mock webhook handlers and sign a sample payload with a fixed secret to
satisfy signature validation

## Testing
- pnpm test __tests__/api/webhook/github.route.test.ts

------
[Codex
Task](https://chatgpt.com/codex/tasks/task_b_693b868dca34832a873724c553f9df48)
## Summary
- add a node suite test for the GitHub issue label auto-resolve handler covering success and error paths
- mock the job enqueue service and require the Redis URL environment variable in tests
- map shared package imports in Jest to support the new test

## Testing
- pnpm test:node -- --runTestsByPath __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts


------
[Codex Task](https://chatgpt.com/codex/tasks/task_b_693b8694741c832abea909a54c707e50)
…abel-followup-b1d670eb' into codex/add-jest-test-for-autoresolveissue-handler
…erWithDockerode: accept argv arrays to avoid shell interpolation; keep string support for backward compatibility\n- createDependentPRWorkflow: switch to argv-based git commands; validate push result and fail workflow on push errors; wrap container cleanup in try/catch to avoid masking original errors\n- Orchestrator: pass numeric installationId to Octokit auth\n- Tests: fix handler.autoResolveIssue test types (import Job, use role in messages); add missing REDIS_URL validation test for PR label handler\n- Jest config: remove duplicate moduleNameMapper entry for shared/*
@youngchingjui youngchingjui added the AI generated AI-generated Pull Requests label Dec 15, 2025 — with IssueToPR.dev
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This PR implements a webhook-based trigger for creating dependent PRs. When a GitHub PR receives the "i2pr: update pr" label, the webhook enqueues a job that orchestrates the dependent PR creation workflow through worker processes, coordinating authentication, repository management, and an AI agent to generate changes.

Changes

Cohort / File(s) Summary
GitHub Webhook Route & Handler
app/api/webhook/github/route.ts, lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
Added PR labeled event routing and handler that validates REDIS_URL, extracts payload fields (owner, repo, pullNumber, login), and enqueues a createDependentPR job to the workflow-jobs queue.
Worker Handler & Orchestrator
apps/workers/workflow-workers/src/handler.ts, apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts
Extended handler to dispatch createDependentPR jobs; added orchestrator that initializes Neo4j settings reader, GitHub app authentication, installation token, and invokes createDependentPRWorkflow with configured adapters.
Workflow Implementation
shared/src/usecases/workflows/createDependentPR.ts
New workflow that gathers PR details, diffs, comments, reviews; sets up container workspace; checks out PR head to dedicated branch; runs DependentPRAgent with rich context; pushes branch when token available.
Agent & Tooling
shared/src/lib/agents/DependentPRAgent.ts, `shared/src/lib/tools/(CreatePRTool
SyncBranchTool).ts`
Git & Ports
shared/src/ports/git/checkoutCommit.ts, shared/src/adapters/git/containerCheckoutCommit.ts
Added CheckoutCommitPort interface and container-based adapter for fetching, verifying, and checking out commits into a target branch with structured error handling.
Job Schema & Types
shared/src/entities/events/Job.ts
Added CreateDependentPRJob schema and type to the JobEventSchema discriminated union.
Worker Infrastructure
`apps/workers/workflow-workers/src/(redis
neo4j).ts, apps/workers/workflow-workers/src/(helper
Neo4j Adapter
shared/src/adapters/neo4j/dataSource.ts
Added Neo4jDataSource interface exposing getDriver() and getSession(mode) methods.
Jest Configuration & Paths
__tests__/config/jest.config.base.ts, shared/tsconfig.json, tsconfig.json
Refined moduleNameMapper with explicit per-subpath aliases and added transforms for TypeScript; added @/shared path alias mappings.
Import Path Updates
`lib/(fetch
github)/*.ts, shared/src/lib/github/(content
Token Extraction
`lib/(fetch
github)/issues.ts`
Telemetry & Docker
shared/src/lib/github/content.ts, shared/src/lib/docker.ts
Removed withTiming wrapper from checkBranchExists; reformatted docker.ts comments with no functional changes.
Test Files
__tests__/api/webhook/github.route.test.ts, __tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts, __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts, __tests__/apps/workers/handler.autoResolveIssue.test.ts, __tests__/lib/workers/createDependentPR.worker.test.ts
Added comprehensive tests validating GitHub webhook route signature verification, handler job enqueueing, worker orchestrator invocation, and status publishing.

Sequence Diagram(s)

sequenceDiagram
    actor GitHub
    participant Route as Webhook Route
    participant Handler as Label Handler
    participant Queue as Job Queue
    participant Worker as Worker Process
    participant Orchestrator as Orchestrator
    participant Neo4j as Neo4j
    participant Git as Git/Container
    participant Workflow as createDependentPRWorkflow
    participant Agent as DependentPRAgent

    GitHub->>Route: POST /api/webhook/github<br/>(PR labeled: "i2pr: update pr")
    Route->>Route: Verify signature
    Route->>Handler: dispatch payload + installationId
    
    Handler->>Handler: Validate REDIS_URL<br/>Extract fields
    Handler->>Queue: addJob(createDependentPR,<br/>payload data)
    Queue-->>Handler: job enqueued
    Handler-->>Route: return {status: "noop"}
    Route-->>GitHub: 200 OK

    rect rgba(100, 150, 200, 0.2)
    Note over Worker,Agent: Async Job Processing
    Queue->>Worker: poll createDependentPR job
    Worker->>Orchestrator: invoke(jobId, jobData)
    
    Orchestrator->>Neo4j: initialize settings reader
    Orchestrator->>Git: load GitHub App key<br/>init Octokit
    Orchestrator->>Git: create installation token
    Orchestrator->>Git: setup container checkout
    
    Orchestrator->>Workflow: invoke with adapters
    Workflow->>Git: fetch PR details, diffs,<br/>comments, reviews
    Workflow->>Git: checkout PR head<br/>to dependent branch
    Workflow->>Agent: initialize with context
    Workflow->>Agent: run agent
    
    Agent->>Agent: apply changes via tools<br/>(files, branches, PR)
    Agent-->>Workflow: completion
    Workflow->>Git: push dependent branch
    Workflow-->>Orchestrator: branch name
    
    Orchestrator->>Worker: return branch info
    Worker->>Queue: publish completion status
    end
Loading

Possibly related PRs

  • #1236: Modifies the dependent-PR workflow and DependentPRAgent to update agent tooling and workflow behavior.
  • #1231: Adds the DependentPRAgent and related dependent-PR workflow orchestration with shared tooling setup.
  • #1440: Modifies the same webhook route to add the handlePullRequestLabelCreateDependentPR handler and wires PR-label "i2pr: update pr" events.

Suggested labels

dependent-pr

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Extensive out-of-scope changes detected: worker infrastructure refactoring (Redis/Neo4j), agent enhancements, git adapters, import path migrations unrelated to PR label webhook feature. Remove worker infrastructure (helper.ts, index.ts, redis.ts, neo4j.ts), agent/workflow changes, git adapters, and import migrations. Keep only PR label webhook handler, job entity schema, and direct webhook integration.
✅ Passed checks (1 passed)
Check name Status Explanation
Linked Issues check ✅ Passed PR implements all core requirements: webhook handler for PR label events, job enqueuing to workflow queue, and integration into webhook route with proper environment/field validation.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac7d23e and f950848.

📒 Files selected for processing (1)
  • shared/src/lib/docker.ts
✅ Files skipped from review due to trivial changes (1)
  • shared/src/lib/docker.ts

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (8)
__tests__/lib/workers/createDependentPR.worker.test.ts (1)

15-33: Consider strengthening test assertions.

The test validates the basic routing but could be more robust:

  1. Line 18: Using any type weakens type safety. Consider using the actual Job type from BullMQ or a proper test fixture.
  2. Line 31: The test verifies createDependentPR was called but doesn't validate the arguments. Consider adding:
    expect(createDependentPR).toHaveBeenCalledWith("job-123", {
      repoFullName: "owner/repo",
      pullNumber: 55,
      githubLogin: "octocat",
      githubInstallationId: "123",
    })
  3. Lines 7-12: The Job entity mock that preserves the actual implementation doesn't appear to serve a purpose. Consider removing it unless there's a specific reason to keep it.
__tests__/apps/workers/handler.autoResolveIssue.test.ts (1)

27-31: Consider using proper message types instead of as any.

The explicit as const on the role is good for type safety, but the as any cast on line 31 weakens type checking. If the autoResolveIssue orchestrator returns a specific message type (e.g., ChatCompletionMessageParam[]), consider importing and using that type instead of casting to any.

__tests__/lib/webhook/label.autoResolveIssue.handler.test.ts (1)

25-28: Add afterAll to restore original environment.

The beforeEach resets process.env before each test, but if tests in this file modify the environment and fail mid-execution, subsequent test suites could inherit the modified state. Consider adding an afterAll hook to ensure cleanup.

   beforeEach(() => {
     jest.restoreAllMocks()
     process.env = { ...originalEnv }
   })
+
+  afterAll(() => {
+    process.env = originalEnv
+  })
__tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts (2)

4-6: Consider adding beforeEach to reset mock state.

The module-level mock with mockResolvedValue persists across tests. If addJob is called in one test and assertions in another test check call counts, this could cause flaky tests. Consider adding a beforeEach to clear mock state.

 jest.mock("shared/services/job", () => ({
   addJob: jest.fn().mockResolvedValue("job-id-123"),
 }))

 describe("handlePullRequestLabelCreateDependentPR", () => {
   const installationId = "123456"
+
+  beforeEach(() => {
+    jest.clearAllMocks()
+  })

43-52: Consider asserting the specific error message.

The test uses generic toThrow() without verifying the error message. For better test specificity, assert the expected message.

     await expect(
       handlePullRequestLabelCreateDependentPR({
         payload: makePayload({ repository: { name: "", owner: { login: "" } } }),
         installationId,
       })
-    ).rejects.toThrow()
+    ).rejects.toThrow("Missing required fields for createDependentPR job")
__tests__/api/webhook/github.route.test.ts (1)

25-30: Consider grouping mocks before imports for clarity.

While Jest hoists jest.mock() calls, placing the handleIssueLabelAutoResolve mock after its import (line 26 before line 28) can be confusing. Consider moving this mock to join the other mocks at lines 7-24 for consistency.

 jest.mock("@/lib/webhook/github/handlers/repository/edited.revalidateRepoCache.handler", () => ({
   handleRepositoryEditedRevalidate: jest.fn(),
 }))
+jest.mock("@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler", () => ({
+  handleIssueLabelAutoResolve: jest.fn(),
+}))
 import { POST } from "@/app/api/webhook/github/route"
 import { handleIssueLabelAutoResolve } from "@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler"
-
-jest.mock("@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler", () => ({
-  handleIssueLabelAutoResolve: jest.fn(),
-}))
shared/src/usecases/workflows/createDependentPR.ts (2)

144-156: Approve with note on credential handling.

The session token embedded in the git remote URL is standard practice for GitHub App authentication. The token is short-lived and scoped to the installation.

Consider adding --quiet flag to suppress any potential credential echoing in logs, though git typically handles this securely:

         command: [
           "git",
           "remote",
           "set-url",
+          "--quiet",
           "origin",
           `https://x-access-token:${sessionToken}@github.com/${repoFullName}.git`,
         ],

222-238: Consider using consistent timestamp formatting.

Using toLocaleString() (lines 225-226, 234-235) produces locale-dependent output that varies based on server configuration. For reproducibility and clarity, consider ISO format:

-          `Comment ${i + 1} by ${c.user?.login || "unknown"} at ${new Date(
-            c.created_at || new Date().toISOString()
-          ).toLocaleString()}\n${c.body}`
+          `Comment ${i + 1} by ${c.user?.login || "unknown"} at ${
+            c.created_at || new Date().toISOString()
+          }\n${c.body}`

However, this is a minor concern since the timestamps are for agent context only.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bbca5a8 and f55f34a.

📒 Files selected for processing (17)
  • __tests__/api/webhook/github.route.test.ts (1 hunks)
  • __tests__/apps/workers/handler.autoResolveIssue.test.ts (1 hunks)
  • __tests__/config/jest.config.base.ts (1 hunks)
  • __tests__/config/jest.config.node.ts (1 hunks)
  • __tests__/lib/utils/setupEnv.test.ts (4 hunks)
  • __tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts (1 hunks)
  • __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts (1 hunks)
  • __tests__/lib/workers/createDependentPR.worker.test.ts (1 hunks)
  • app/api/webhook/github/route.ts (3 hunks)
  • apps/workers/workflow-workers/src/handler.ts (3 hunks)
  • apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (1 hunks)
  • lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (1 hunks)
  • lib/webhook/github/types.ts (2 hunks)
  • shared/src/entities/events/Job.ts (1 hunks)
  • shared/src/lib/agents/DependentPRAgent.ts (1 hunks)
  • shared/src/lib/docker.ts (4 hunks)
  • shared/src/usecases/workflows/createDependentPR.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx,mts,mjs}

📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)

Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Files:

  • __tests__/config/jest.config.node.ts
  • shared/src/entities/events/Job.ts
  • lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
  • shared/src/lib/agents/DependentPRAgent.ts
  • lib/webhook/github/types.ts
  • app/api/webhook/github/route.ts
  • shared/src/lib/docker.ts
  • __tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts
  • __tests__/apps/workers/handler.autoResolveIssue.test.ts
  • shared/src/usecases/workflows/createDependentPR.ts
  • apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts
  • __tests__/lib/workers/createDependentPR.worker.test.ts
  • __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts
  • __tests__/api/webhook/github.route.test.ts
  • apps/workers/workflow-workers/src/handler.ts
  • __tests__/config/jest.config.base.ts
  • __tests__/lib/utils/setupEnv.test.ts
🧠 Learnings (6)
📓 Common learnings
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1282
File: shared/src/ports/events/publisher.ts:16-19
Timestamp: 2025-09-22T09:24:26.840Z
Learning: youngchingjui prefers to manage PR scope tightly and defer technical debt improvements to future PRs rather than expanding the current PR's scope, even for related consistency issues.
📚 Learning: 2025-09-12T01:44:51.070Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1241
File: app/api/webhook/github/route.ts:85-90
Timestamp: 2025-09-12T01:44:51.070Z
Learning: In the webhook handlers at app/api/webhook/github/route.ts, runWithInstallationId is intentionally not awaited to allow fire-and-forget background processing of long-running workflows, avoiding webhook timeouts. The team plans to migrate these to proper queues/workers in a separate issue later.

Applied to files:

  • lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
  • app/api/webhook/github/route.ts
  • __tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts
  • __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts
  • __tests__/api/webhook/github.route.test.ts
📚 Learning: 2025-09-10T07:15:30.472Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1231
File: lib/agents/DependentPRAgent.ts:61-61
Timestamp: 2025-09-10T07:15:30.472Z
Learning: In lib/agents/DependentPRAgent.ts, the constructor uses `super({ model: "gpt-5", ...base })` intentionally to allow base.model to override "gpt-5" if needed. GPT-5 should be treated as the default model, not enforced, to maintain flexibility for future use cases.

Applied to files:

  • shared/src/lib/agents/DependentPRAgent.ts
📚 Learning: 2025-09-11T09:11:14.319Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1214
File: lib/webhook.ts:184-189
Timestamp: 2025-09-11T09:11:14.319Z
Learning: The autoResolveIssue workflow signature is autoResolveIssue(params, ports) where params contains {issue, repository, jobId} and ports contains {auth: AuthReaderPort, settings: SettingsReaderPort, eventBus?: EventBusPort}.

Applied to files:

  • __tests__/apps/workers/handler.autoResolveIssue.test.ts
  • __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts
📚 Learning: 2025-09-11T09:11:14.319Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1214
File: lib/webhook.ts:184-189
Timestamp: 2025-09-11T09:11:14.319Z
Learning: The autoResolveIssue workflow in the codebase follows clean architecture principles with a two-argument signature: autoResolveIssue(params, ports), where ports contains auth and settings adapters.

Applied to files:

  • __tests__/apps/workers/handler.autoResolveIssue.test.ts
  • __tests__/lib/webhook/label.autoResolveIssue.handler.test.ts
📚 Learning: 2025-09-04T14:57:04.612Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/code-structure.mdc:0-0
Timestamp: 2025-09-04T14:57:04.612Z
Learning: Applies to **/*.{ts,tsx,js,jsx,mts,mjs} : Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Applied to files:

  • __tests__/config/jest.config.base.ts
🧬 Code graph analysis (8)
lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (3)
lib/webhook/github/types.ts (1)
  • PullRequestPayload (51-51)
shared/src/entities/Queue.ts (1)
  • WORKFLOW_JOBS_QUEUE (3-3)
shared/src/services/job.ts (1)
  • addJob (10-19)
app/api/webhook/github/route.ts (1)
lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (1)
  • handlePullRequestLabelCreateDependentPR (11-51)
__tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts (3)
lib/webhook/github/types.ts (1)
  • PullRequestPayload (51-51)
lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (1)
  • handlePullRequestLabelCreateDependentPR (11-51)
shared/src/services/job.ts (1)
  • addJob (10-19)
apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (6)
apps/workers/workflow-workers/src/helper.ts (2)
  • publishJobStatus (48-54)
  • getEnvVar (43-46)
lib/neo4j/index.ts (1)
  • neo4jDs (27-33)
shared/src/adapters/neo4j/dataSource.ts (1)
  • createNeo4jDataSource (11-29)
shared/src/adapters/neo4j/repositories/SettingsReaderAdapter.ts (1)
  • makeSettingsReaderAdapter (28-54)
shared/src/adapters/ioredis/EventBusAdapter.ts (1)
  • EventBusAdapter (10-32)
shared/src/auth.ts (1)
  • setAccessToken (11-13)
__tests__/lib/workers/createDependentPR.worker.test.ts (2)
apps/workers/workflow-workers/src/handler.ts (1)
  • handler (24-86)
apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (1)
  • createDependentPR (38-103)
__tests__/lib/webhook/label.autoResolveIssue.handler.test.ts (3)
lib/webhook/github/types.ts (1)
  • IssuesPayload (33-33)
lib/webhook/github/handlers/issue/label.autoResolveIssue.handler.ts (1)
  • handleIssueLabelAutoResolve (11-47)
shared/src/entities/Queue.ts (1)
  • WORKFLOW_JOBS_QUEUE (3-3)
__tests__/api/webhook/github.route.test.ts (1)
lib/webhook/github/handlers/issue/label.autoResolveIssue.handler.ts (1)
  • handleIssueLabelAutoResolve (11-47)
apps/workers/workflow-workers/src/handler.ts (2)
apps/workers/workflow-workers/src/helper.ts (1)
  • publishJobStatus (48-54)
apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (1)
  • createDependentPR (38-103)
🔇 Additional comments (29)
__tests__/config/jest.config.node.ts (1)

13-17: LGTM! Test pattern extension looks good.

The addition of the apps test pattern aligns with the new worker handler tests introduced in this PR and follows the existing naming conventions.

shared/src/entities/events/Job.ts (1)

37-55: LGTM! Schema definition follows established patterns.

The CreateDependentPRJobSchema is well-structured and consistent with existing job schemas in the file. The integration into the discriminated union is correct.

__tests__/lib/utils/setupEnv.test.ts (2)

7-26: LGTM! Type definitions improve mock accuracy.

The explicit typing for ExecCallback, ExecException, and ExecOptions makes the test mock more type-safe and aligns properly with Node's child_process module types.


36-87: LGTM! Mock implementation correctly handles both callback signatures.

The mock properly handles both exec(cmd, callback) and exec(cmd, options, callback) signatures through the union type and type guard. The {} as ChildProcess cast is acceptable for test purposes since the actual ChildProcess instance behavior isn't exercised.

__tests__/apps/workers/handler.autoResolveIssue.test.ts (1)

26-58: LGTM! Test comprehensively validates the autoResolveIssue flow.

The test properly verifies:

  • Orchestrator invocation with correct parameters
  • Status publishing at key workflow stages
  • Correct transformation of messages to return value

The test structure is solid and provides good coverage of the handler's behavior.

__tests__/config/jest.config.base.ts (1)

11-16: LGTM! Path aliases refined for shared module access.

The changes introduce:

  1. A new shared/* alias (line 12) alongside the existing @shared/* (line 11) — supporting both import styles
  2. A composite pattern (lines 14-15) for specific subdirectories within shared/src
  3. A generic @/* fallback (line 16)

This approach provides flexible import paths while maintaining specific routing for shared modules. The dual aliases (@shared and shared) appear intentional to support different import conventions across the codebase.

lib/webhook/github/types.ts (1)

35-50: LGTM! Payload schema appropriately extended for PR label events.

The additions support the new "labeled" PR webhook flow:

  • Line 37: Top-level number for PR number in label events
  • Line 38: label object for the applied label
  • Line 39: sender for the user who triggered the event
  • Line 43: Nested number in pull_request for other event types

All fields are properly optional, maintaining backward compatibility with existing PR event handlers.

apps/workers/workflow-workers/src/handler.ts (1)

70-75: LGTM! Handler integration follows established patterns.

The createDependentPR case is well-integrated:

  • Consistent with existing job handling patterns
  • Appropriate status messages at start and completion
  • Returns the created branch name for tracking
  • Error handling covered by the outer try-catch block
__tests__/lib/webhook/label.autoResolveIssue.handler.test.ts (3)

30-56: LGTM!

The test properly verifies the job enqueue behavior with correct queue name, job payload structure, and Redis URL passthrough.


58-67: LGTM!

Correctly tests the REDIS_URL validation path.


69-86: LGTM!

Good coverage for the missing required fields validation path. The type assertion is appropriate here to simulate malformed webhook payloads.

__tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts (2)

24-41: LGTM!

The test correctly verifies the job enqueue with proper payload structure including repoFullName, pullNumber, githubLogin, and githubInstallationId.


54-66: LGTM with minor note.

The test correctly validates the REDIS_URL check. The conditional restoration on line 65 is acceptable, though using a beforeEach/afterEach pattern for environment management would be more consistent with the other test file.

shared/src/lib/docker.ts (2)

129-136: LGTM! Good security improvement.

The updated documentation clearly explains the two execution modes: shell-wrapped for string commands (backward compatibility) and direct argv for array commands (avoiding shell interpolation). This enables safer execution of user-controlled inputs.


179-188: LGTM!

The implementation correctly distinguishes between array and string commands. When an array is provided, it's passed directly to Docker exec, preventing shell interpolation attacks on user-controlled inputs like branch names or refs.

app/api/webhook/github/route.ts (1)

171-189: LGTM!

The new labeled case follows the established pattern from the issues event handler. The case-insensitive label matching and installationId extraction are consistent with other handlers. Awaiting the handler is appropriate since it only enqueues a job (fast operation), unlike the fire-and-forget pattern noted in learnings for long-running workflows.

lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (1)

1-51: LGTM!

The handler follows the established pattern from handleIssueLabelAutoResolve. Key strengths:

  • Proper environment validation for REDIS_URL
  • Defensive extraction of pullNumber from multiple payload locations
  • Clear error message listing all missing fields
  • Correct job data structure matching the orchestrator's CreateDependentPRJobData type
shared/src/lib/agents/DependentPRAgent.ts (3)

86-100: LGTM!

Good defensive programming with try/catch around remote tool attachment. The warning log provides visibility into failures without breaking the agent initialization. The schema validation ensures repository metadata is well-formed before use.


61-61: Noted: GPT-5 default model is intentional.

Per learnings, the super({ model: "gpt-5", ...base }) pattern allows base.model to override the default when needed, maintaining flexibility.


68-70: The fire-and-forget setDeveloperPrompt() pattern is safe in this codebase. Although the method is async and not awaited, the agent is not used immediately after construction—there is substantial processing (data formatting, context building) between instantiation and the await agent.runWithFunctions() call, providing ample time for the prompt to be added to the queue. This same pattern is consistently used across other agents (ThinkerAgent, PlanAndCodeAgent, MergeConflictResolverAgent), indicating it is an intentional design choice.

Likely an incorrect or invalid review comment.

apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (3)

14-29: LGTM!

The in-file userRepo implementation is minimal but sufficient for the settings lookup. The Cypher query is properly parameterized to prevent injection attacks.


67-84: LGTM!

The GitHub App authentication flow is correctly implemented:

  • Number(githubInstallationId) satisfies @octokit/auth-app's type requirements (PR objective)
  • Token validation is thorough before use
  • Global setAccessToken usage is noted as addressed separately in #1409 per PR objectives

38-103: Error handling is already implemented at the handler level. The handler function wraps the orchestrator calls in a try-catch block that publishes failure status via publishJobStatus(job.id, Failed: ${msg}) before re-throwing the error. No changes needed.

Likely an incorrect or invalid review comment.

__tests__/api/webhook/github.route.test.ts (1)

45-80: LGTM! Solid test coverage for webhook routing.

The test correctly validates:

  • Signature verification with HMAC-SHA256
  • Proper routing of labeled issue events to the auto-resolve handler
  • Correct argument passing (installationId as string, payload fields)

The mock request setup and assertions are well-structured.

shared/src/usecases/workflows/createDependentPR.ts (5)

36-46: LGTM!

Clean interface definitions with appropriate optional markers.


58-65: LGTM!

Proper validation of API key result with both success check and value presence before proceeding.


92-108: LGTM!

Good use of Promise.all for parallel fetching of independent PR artifacts.


320-330: LGTM!

Good error handling pattern: cleanup failures are logged as warnings without masking the original error, as noted in the PR objectives.


36-46: No changes needed. The interfaces do not need to be exported. The orchestrator that calls this workflow function constructs the parameter objects inline without importing the type definitions, and no other external consumers reference these types. The function signature itself adequately documents the expected parameter shapes.

Likely an incorrect or invalid review comment.

import { getEnvVar, publishJobStatus } from "../helper"

// Minimal user repository implementation for SettingsReaderAdapter
const userRepo = {
Copy link
Owner Author

Choose a reason for hiding this comment

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

Wait, this is crazy that this is here. Why isn't this embedded directly within makeSettingsReaderAdapter?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
shared/src/usecases/workflows/createDependentPR.ts (1)

304-312: Fix silent skip when sessionToken is missing.

If sessionToken is falsy, the branch push is silently skipped, but the workflow still reports success and returns the branch name. This could mislead callers and cause downstream failures when attempting to create a PR from a non-existent remote branch.

Apply this diff to fail explicitly when the token is missing:

     if (sessionToken) {
       const { exitCode } = await execInContainerWithDockerode({
         name: containerName,
         command: ["git", "push", "-u", "origin", dependentBranch],
       })
       if (exitCode !== 0) {
         throw new Error(`Failed to push branch ${dependentBranch}`)
       }
+    } else {
+      throw new Error(
+        `Cannot push branch ${dependentBranch}: no session token available`
+      )
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f55f34a and f58762a.

📒 Files selected for processing (1)
  • shared/src/usecases/workflows/createDependentPR.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx,mts,mjs}

📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)

Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Files:

  • shared/src/usecases/workflows/createDependentPR.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/git-management.mdc:0-0
Timestamp: 2025-09-04T14:58:54.915Z
Learning: Can create and update pull requests through GitHub API when requested
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1282
File: shared/src/ports/events/publisher.ts:16-19
Timestamp: 2025-09-22T09:24:26.840Z
Learning: youngchingjui prefers to manage PR scope tightly and defer technical debt improvements to future PRs rather than expanding the current PR's scope, even for related consistency issues.
📚 Learning: 2025-09-04T14:57:28.594Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/container-use.mdc:0-0
Timestamp: 2025-09-04T14:57:28.594Z
Learning: When providing the "checkout command for human", ensure it switches to a branch that tracks the container-use remote and note that it will not pull changes.

Applied to files:

  • shared/src/usecases/workflows/createDependentPR.ts
🧬 Code graph analysis (1)
shared/src/usecases/workflows/createDependentPR.ts (6)
shared/src/ports/repositories/settings.reader.ts (1)
  • SettingsReaderPort (9-19)
shared/src/ports/events/eventBus.ts (1)
  • EventBusPort (8-13)
shared/src/ports/events/publisher.ts (1)
  • createWorkflowEventPublisher (8-141)
shared/src/lib/docker.ts (1)
  • execInContainerWithDockerode (138-226)
shared/src/lib/agents/DependentPRAgent.ts (1)
  • DependentPRAgent (49-102)
shared/src/lib/langfuse.ts (1)
  • langfuse (3-6)
🔇 Additional comments (2)
shared/src/usecases/workflows/createDependentPR.ts (2)

1-46: LGTM!

The imports and interface definitions are clean and well-structured.


182-189: LGTM! Exit code check properly implemented.

The git pull exit code is now correctly validated, and the workflow throws an error on failure. This addresses the previous review concern about divergent branches.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
shared/src/usecases/workflows/createDependentPR.ts (3)

163-166: Add exit code check for git fetch.

The git fetch command could fail (network issues, invalid ref, etc.), but the exit code isn't checked. This could leave the workflow with stale remote refs or proceed without the target branch.

Apply this diff to check the fetch result:

-    await execInContainerWithDockerode({
+    const { exitCode: fetchExit } = await execInContainerWithDockerode({
       name: containerName,
       command: ["git", "fetch", "origin", headRef],
     })
+    if (fetchExit !== 0) {
+      throw new Error(`Failed to fetch branch ${headRef} from origin`)
+    }

173-176: Add exit code check for git checkout -b.

The git checkout -b command could fail (permissions, disk space, etc.), but the exit code isn't checked. Continuing without the correct branch would corrupt the workflow state.

Apply this diff:

-      await execInContainerWithDockerode({
+      const { exitCode: checkoutExit } = await execInContainerWithDockerode({
         name: containerName,
         command: ["git", "checkout", "-b", headRef, `origin/${headRef}`],
       })
+      if (checkoutExit !== 0) {
+        throw new Error(`Failed to create and checkout branch ${headRef}`)
+      }

178-181: Add exit code check for git checkout.

The git checkout command could fail, leaving the workflow on the wrong branch. This would cause subsequent commits and pushes to target an incorrect branch.

Apply this diff:

-      await execInContainerWithDockerode({
+      const { exitCode: checkoutExit } = await execInContainerWithDockerode({
         name: containerName,
         command: ["git", "checkout", "-q", headRef],
       })
+      if (checkoutExit !== 0) {
+        throw new Error(`Failed to checkout branch ${headRef}`)
+      }
🧹 Nitpick comments (1)
shared/src/usecases/workflows/createDependentPR.ts (1)

226-258: Simplify fallback date pattern.

The pattern new Date(timestamp || new Date().toISOString()) is redundant. When the timestamp is falsy, new Date().toISOString() creates a string that's immediately passed back to new Date().

Consider simplifying to:

-`Comment ${i + 1} by ${c.user?.login || "unknown"} at ${new Date(
-  c.created_at || new Date().toISOString()
-).toLocaleString()}\n${c.body}`
+`Comment ${i + 1} by ${c.user?.login || "unknown"} at ${
+  c.created_at ? new Date(c.created_at).toLocaleString() : new Date().toLocaleString()
+}\n${c.body}`

Apply the same pattern to reviews (lines 238-240) and review threads (lines 247-249).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f58762a and 67efc21.

📒 Files selected for processing (1)
  • shared/src/usecases/workflows/createDependentPR.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx,mts,mjs}

📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)

Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Files:

  • shared/src/usecases/workflows/createDependentPR.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/git-management.mdc:0-0
Timestamp: 2025-09-04T14:58:54.915Z
Learning: Can create and update pull requests through GitHub API when requested
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1282
File: shared/src/ports/events/publisher.ts:16-19
Timestamp: 2025-09-22T09:24:26.840Z
Learning: youngchingjui prefers to manage PR scope tightly and defer technical debt improvements to future PRs rather than expanding the current PR's scope, even for related consistency issues.
📚 Learning: 2025-09-11T09:11:14.319Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1214
File: lib/webhook.ts:184-189
Timestamp: 2025-09-11T09:11:14.319Z
Learning: The autoResolveIssue workflow signature is autoResolveIssue(params, ports) where params contains {issue, repository, jobId} and ports contains {auth: AuthReaderPort, settings: SettingsReaderPort, eventBus?: EventBusPort}.

Applied to files:

  • shared/src/usecases/workflows/createDependentPR.ts
📚 Learning: 2025-09-04T14:57:28.594Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/container-use.mdc:0-0
Timestamp: 2025-09-04T14:57:28.594Z
Learning: When providing the "checkout command for human", ensure it switches to a branch that tracks the container-use remote and note that it will not pull changes.

Applied to files:

  • shared/src/usecases/workflows/createDependentPR.ts
📚 Learning: 2025-09-04T14:57:28.594Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/container-use.mdc:0-0
Timestamp: 2025-09-04T14:57:28.594Z
Learning: Always inform the user how to view their changes using a 'git checkout' command that does not include container-use/remote.

Applied to files:

  • shared/src/usecases/workflows/createDependentPR.ts
🔇 Additional comments (4)
shared/src/usecases/workflows/createDependentPR.ts (4)

1-46: LGTM: Clean imports and type definitions.

The import organization is logical, and the type definitions for CreateDependentPRParams and Ports are clear and well-structured.


48-125: LGTM: Well-structured initialization with efficient parallel operations.

The initialization logic is clean and follows good practices:

  • Proper dependency injection via ports pattern
  • Early validation of API key with clear error messages
  • Efficient parallel fetching of PR artifacts using Promise.all
  • Proper workflow event emission throughout

299-317: LGTM: Proper push validation and error handling.

The push logic correctly:

  • Checks the exit code and throws on failure
  • Emits a clear warning when sessionToken is unavailable
  • Prevents silent failures

324-343: LGTM: Robust error handling and safe cleanup.

The error handling is well-structured:

  • Proper workflow state transitions on error
  • Cleanup in finally block ensures execution regardless of outcome
  • Cleanup errors are caught and logged without masking the original error

This implementation follows the PR objective for "cleanup safety."

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This is the final PR Bugbot will review for you during this billing cycle

Your free Bugbot reviews will reset on January 19

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

// Attach prompt with reasoning usage enabled by Responses API
this.setDeveloperPrompt(DEVELOPER_PROMPT).catch((error) => {
console.error("Error initializing DependentPRAgent system prompt:", error)
})
Copy link

Choose a reason for hiding this comment

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

Race condition in developer prompt initialization

The DependentPRAgent constructor calls setDeveloperPrompt() without awaiting it. This async method internally awaits trackInput() (a Neo4j operation) before pushing to inputQueue. When the workflow later calls await agent.addInput() for the user message, both operations race to complete their Neo4j calls. Whichever finishes first pushes to the queue first. If the user message wins the race, it appears before the developer prompt in inputQueue, causing the LLM to receive messages in the wrong order and potentially behave incorrectly.

Additional Locations (1)

Fix in Cursor Fix in Web

@youngchingjui youngchingjui marked this pull request as draft December 29, 2025 03:34
youngchingjui added a commit that referenced this pull request Dec 29, 2025
## Summary
Implement webhook handling for PR labels to trigger the createDependentPR workflow when PRs are labeled with "I2PR: Update PR".

## Features Implemented

### 🔗 Webhook Handler Foundation
- **New Handler**: `handlePullRequestLabelCreateDependentPR` enqueues createDependentPR jobs
- **Enhanced Schema**: Extended `PullRequestPayloadSchema` with `label`, `number`, and `sender` fields
- **Route Integration**: Added PR labeled event routing in webhook endpoint

### 🎯 How It Works
1. **GitHub Event**: PR gets labeled with "I2PR: Update PR" 
2. **Webhook Processing**: Validates payload and extracts job data
3. **Job Queuing**: Enqueues `createDependentPR` job with:
   - `repoFullName` (e.g., "owner/repo")
   - `pullNumber` (PR number)
   - `githubLogin` (who applied the label)  
   - `githubInstallationId` (for GitHub App auth)
4. **Worker Processing**: (Next PR) Worker will create dependent branch/PR

### 🧪 Test Coverage
- [x] Handler validates required fields and environment
- [x] Handler enqueues jobs with correct payload structure
- [x] Webhook route correctly routes labeled events
- [x] Integration test covers full webhook → handler flow

## Technical Details

### New Files
```
lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
__tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts  
__tests__/api/webhook/github.route.test.ts
```

### Modified Files
```
app/api/webhook/github/route.ts - Added labeled event routing
lib/webhook/github/types.ts - Extended PullRequestPayloadSchema
```

### Example Usage
When a PR is labeled "I2PR: Update PR", this triggers:
```typescript
// Enqueues Redis job:
{
  name: "createDependentPR",
  data: {
    repoFullName: "owner/repo",
    pullNumber: 42,
    githubLogin: "developer", 
    githubInstallationId: "12345"
  }
}
```

## Context & Sequence
This PR is part of breaking down the large PR #1413 into manageable pieces:

1. ✅ **PR #1437**: Test infrastructure fixes
2. ✅ **PR #1438**: Container security enhancements  
3. ✅ **This PR**: Webhook handler foundation
4. 🔄 **Next**: Worker infrastructure (Redis/Neo4j setup)
5. 🔄 **Final**: CreateDependentPR core workflow

## Testing
```bash
pnpm test __tests__/lib/webhook/handlers/pullRequest/
pnpm test __tests__/api/webhook/github.route.test.ts
```

Addresses the webhook handling requirements from **Issue #1399**.

🤖 Generated with [Claude Code](https://claude.ai/code)

---

Update: Adjust handler to no-op per review feedback

- Addressed RLC-001 and RLC-002: The worker does not yet recognize a `createDependentPR` job name. To keep this PR scoped to webhook routing and avoid enqueueing unknown jobs, the handler now performs a no-op.
- Changes:
  - `handlePullRequestLabelCreateDependentPR` no longer enqueues a job. It validates required fields, logs a clear message indicating the webhook was received, and returns a small `{ status: "noop", ... }` result.
  - Updated unit tests to reflect the no-op behavior (removed Redis/job enqueue expectations; assert logging/return value instead).
- Route tests remain the same (they mock the handler and only assert routing/invocation).

Rationale
- This keeps the boundary clean and avoids enqueueing jobs that would fail in the worker until the job schema/handler support is added in a follow-up PR.

Verification
- Jest tests for the updated handler and webhook route pass locally:
  - `__tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts`
  - `__tests__/api/webhook/github.route.test.ts`

Next steps
- Follow-up PR to register the `createDependentPR` job type in the worker and wire up the actual enqueue logic.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Pull requests can now be automatically processed when labeled with "i2pr: update pr"
  * Enhanced webhook system to recognize and route pull request label events with proper signature verification

* **Tests**
  * Added comprehensive unit tests for webhook routing and signature verification
  * Added tests for pull request label event handling, including validation of required payload fields

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
shared/src/lib/agents/DependentPRAgent.ts (1)

67-70: Race condition: setDeveloperPrompt() is not awaited.

The async setDeveloperPrompt() method is called without await, which can cause a race condition where subsequent addInput() calls may complete before the developer prompt is pushed to the input queue, resulting in incorrect message ordering for the LLM.

Consider making initialization synchronous or ensuring the prompt is set before any user input is added.

🧹 Nitpick comments (6)
__tests__/api/webhook/github.route.test.ts (4)

7-43: Consider grouping all mocks before imports.

The mock for handleIssueLabelAutoResolve (lines 38-43) appears after importing the function (line 35). While Jest hoisting makes this work correctly, grouping all jest.mock() calls before any imports improves readability and follows common convention.

🔎 Suggested reorg

Move lines 38-43 to appear before line 34:

+jest.mock(
+  "@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler",
+  () => ({
+    handleIssueLabelAutoResolve: jest.fn(),
+  })
+)
 jest.mock(
   "@/lib/webhook/github/handlers/repository/edited.revalidateRepoCache.handler",
   () => ({
     handleRepositoryEditedRevalidate: jest.fn(),
   })
 )
 import { POST } from "@/app/api/webhook/github/route"
 import { handleIssueLabelAutoResolve } from "@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler"
 import { handlePullRequestLabelCreateDependentPR } from "@/lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler"

-jest.mock(
-  "@/lib/webhook/github/handlers/issue/label.autoResolveIssue.handler",
-  () => ({
-    handleIssueLabelAutoResolve: jest.fn(),
-  })
-)

58-96: Strengthen routing verification by asserting other handlers weren't called.

The test verifies that handleIssueLabelAutoResolve is called, but doesn't confirm that other mocked handlers (e.g., handlePullRequestLabelCreateDependentPR, handleIssueLabelResolve) remained uncalled. Adding these assertions would catch routing bugs where the wrong handler is invoked or multiple handlers fire unexpectedly.

🔎 Example assertions to add

After line 95, add:

expect(handlePullRequestLabelCreateDependentPR).not.toHaveBeenCalled()
expect(handleIssueLabelResolve).not.toHaveBeenCalled()
// Add for other mocked handlers as needed

Note: You'll need to import the mocked handlers or access them via jest.mocked().


98-136: Strengthen routing verification by asserting other handlers weren't called.

Similar to the first test, verifying that handleIssueLabelAutoResolve and other mocked handlers weren't invoked would strengthen the test's ability to detect routing logic errors.

🔎 Example assertions to add

After line 135, add:

expect(handleIssueLabelAutoResolve).not.toHaveBeenCalled()
expect(handleIssueLabelResolve).not.toHaveBeenCalled()
// Add for other mocked handlers as needed

45-136: Consider adding negative test cases for comprehensive coverage.

The current tests effectively verify happy-path routing logic. To further improve robustness, consider adding test cases for:

  • Invalid webhook signatures
  • Missing required fields (e.g., installation ID, repository info)
  • Unexpected event types
  • Malformed payloads

These cases would help ensure the route handles errors gracefully and provides clear failure signals.

__tests__/tsconfig.json (1)

17-18: Consider consolidating duplicate path aliases.

Both @/shared/* and @shared/* map to the same location. While this may support a gradual migration, maintaining both aliases long-term can lead to inconsistent import styles across the codebase.

If both are needed temporarily for migration, consider documenting which alias is preferred and planning to deprecate one in a future PR.

shared/tsconfig.json (1)

11-12: Duplicate alias pattern.

Similar to the test configuration, both @shared/* and @/shared/* resolve to the same location. This duplication appears across multiple tsconfig files in this PR.

Consider standardizing on a single alias pattern repository-wide for consistency and easier maintenance.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a227d5a and 3307c31.

📒 Files selected for processing (21)
  • __tests__/api/webhook/github.route.test.ts
  • __tests__/apps/workers/handler.autoResolveIssue.test.ts
  • __tests__/config/jest.config.base.ts
  • __tests__/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts
  • __tests__/lib/workers/createDependentPR.worker.test.ts
  • __tests__/tsconfig.json
  • apps/workers/workflow-workers/src/handler.ts
  • lib/fetch/github/issues.ts
  • lib/github/issues.ts
  • lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
  • lib/webhook/github/types.ts
  • shared/src/lib/agents/DependentPRAgent.ts
  • shared/src/lib/github/content.ts
  • shared/src/lib/github/index.ts
  • shared/src/lib/github/pullRequests.ts
  • shared/src/lib/tools/CreatePRTool.ts
  • shared/src/services/job.ts
  • shared/src/services/queue.ts
  • shared/src/usecases/workflows/createDependentPR.ts
  • shared/tsconfig.json
  • tsconfig.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • tests/lib/workers/createDependentPR.worker.test.ts
  • lib/webhook/github/types.ts
  • tests/apps/workers/handler.autoResolveIssue.test.ts
  • shared/src/usecases/workflows/createDependentPR.ts
  • tests/lib/webhook/handlers/pullRequest/label.createDependentPR.handler.test.ts
  • lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx,mts,mjs}

📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)

Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Files:

  • shared/src/services/queue.ts
  • __tests__/api/webhook/github.route.test.ts
  • lib/github/issues.ts
  • shared/src/lib/github/pullRequests.ts
  • shared/src/lib/tools/CreatePRTool.ts
  • apps/workers/workflow-workers/src/handler.ts
  • shared/src/services/job.ts
  • __tests__/config/jest.config.base.ts
  • shared/src/lib/github/index.ts
  • shared/src/lib/github/content.ts
  • lib/fetch/github/issues.ts
  • shared/src/lib/agents/DependentPRAgent.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1282
File: shared/src/ports/events/publisher.ts:16-19
Timestamp: 2025-09-22T09:24:26.840Z
Learning: youngchingjui prefers to manage PR scope tightly and defer technical debt improvements to future PRs rather than expanding the current PR's scope, even for related consistency issues.
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1241
File: app/api/webhook/github/route.ts:85-90
Timestamp: 2025-09-12T01:44:51.070Z
Learning: In the webhook handlers at app/api/webhook/github/route.ts, runWithInstallationId is intentionally not awaited to allow fire-and-forget background processing of long-running workflows, avoiding webhook timeouts. The team plans to migrate these to proper queues/workers in a separate issue later.
📚 Learning: 2025-09-04T14:57:04.612Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/code-structure.mdc:0-0
Timestamp: 2025-09-04T14:57:04.612Z
Learning: Applies to **/*.{ts,tsx,js,jsx,mts,mjs} : Avoid barrel files: do not create files whose sole purpose is to re-export symbols from multiple modules; prefer importing from original module paths to maintain clear dependency graphs and better tree-shaking

Applied to files:

  • tsconfig.json
  • shared/tsconfig.json
  • __tests__/tsconfig.json
  • shared/src/lib/tools/CreatePRTool.ts
  • __tests__/config/jest.config.base.ts
  • shared/src/lib/github/index.ts
📚 Learning: 2025-09-12T01:44:51.070Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1241
File: app/api/webhook/github/route.ts:85-90
Timestamp: 2025-09-12T01:44:51.070Z
Learning: In the webhook handlers at app/api/webhook/github/route.ts, runWithInstallationId is intentionally not awaited to allow fire-and-forget background processing of long-running workflows, avoiding webhook timeouts. The team plans to migrate these to proper queues/workers in a separate issue later.

Applied to files:

  • __tests__/api/webhook/github.route.test.ts
📚 Learning: 2025-09-04T14:58:54.915Z
Learnt from: CR
Repo: youngchingjui/issue-to-pr PR: 0
File: .cursor/rules/git-management.mdc:0-0
Timestamp: 2025-09-04T14:58:54.915Z
Learning: Can create and update pull requests through GitHub API when requested

Applied to files:

  • shared/src/lib/tools/CreatePRTool.ts
📚 Learning: 2025-09-10T07:15:30.472Z
Learnt from: youngchingjui
Repo: youngchingjui/issue-to-pr PR: 1231
File: lib/agents/DependentPRAgent.ts:61-61
Timestamp: 2025-09-10T07:15:30.472Z
Learning: In lib/agents/DependentPRAgent.ts, the constructor uses `super({ model: "gpt-5", ...base })` intentionally to allow base.model to override "gpt-5" if needed. GPT-5 should be treated as the default model, not enforced, to maintain flexibility for future use cases.

Applied to files:

  • shared/src/lib/agents/DependentPRAgent.ts
🧬 Code graph analysis (3)
__tests__/api/webhook/github.route.test.ts (2)
lib/webhook/github/handlers/issue/label.autoResolveIssue.handler.ts (1)
  • handleIssueLabelAutoResolve (11-47)
lib/webhook/github/handlers/pullRequest/label.createDependentPR.handler.ts (1)
  • handlePullRequestLabelCreateDependentPR (10-62)
apps/workers/workflow-workers/src/handler.ts (2)
apps/workers/workflow-workers/src/helper.ts (1)
  • publishJobStatus (49-55)
apps/workers/workflow-workers/src/orchestrators/createDependentPR.ts (1)
  • createDependentPR (39-108)
shared/src/lib/agents/DependentPRAgent.ts (2)
shared/src/lib/tools/CreatePRTool.ts (1)
  • createCreatePRTool (74-85)
lib/agents/DependentPRAgent.ts (1)
  • DependentPRAgent (58-121)
🔇 Additional comments (17)
lib/github/issues.ts (1)

136-136: LGTM: Cleaner token extraction with nullish coalescing.

The change from a questionable cast to the nullish coalescing operator (?? null) is a clear improvement. It explicitly normalizes undefined to null, yielding a predictable string | null type, and is more idiomatic TypeScript.

lib/fetch/github/issues.ts (1)

23-23: LGTM: Consistent token handling improvement.

This change mirrors the improvement in lib/github/issues.ts, replacing the redundant cast with nullish coalescing for clearer, more consistent token extraction across the codebase.

shared/src/lib/agents/DependentPRAgent.ts (5)

1-13: LGTM!

Imports are organized and follow the guideline to import directly from original module paths rather than barrel files.


15-35: LGTM!

The developer prompt is well-structured with clear objectives, operating principles, and required end state that align with the dependent PR workflow use case.


37-47: LGTM!

Interface is well-typed with helpful JSDoc comments explaining the purpose of each optional parameter.


72-100: LGTM!

Tool attachment logic is well-structured:

  • Core workspace tools are always attached
  • Container exec tool is conditionally added based on environment type
  • Remote tools are properly guarded with try-catch and credential checks
  • Clear separation between tools requiring sessionToken (SyncBranchTool) and those using app-level auth (CreatePRTool)

102-104: LGTM!

Providing both named and default exports allows flexibility in how consumers import the class.

apps/workers/workflow-workers/src/handler.ts (2)

23-29: LGTM! Generic job interface decouples handler from BullMQ.

The new JobInput<D> interface and generic handler signature provide a clean abstraction that decouples the handler from the BullMQ-specific Job type, improving testability and flexibility.


75-80: LGTM! New job case follows established patterns and is properly typed.

The createDependentPR case is well-integrated:

  • JobEventSchema in shared/src/entities/events/Job.ts includes CreateDependentPRJobSchema with the correct discriminator literal "createDependentPR"
  • CreateDependentPRJobData type properly defines all required fields: repoFullName, pullNumber, githubLogin, and githubInstallationId, matching the schema definition
  • Follows the same status publishing pattern as existing cases
  • Correctly invokes the orchestrator with job ID and data
  • Returns the created branch name as expected
shared/src/lib/tools/CreatePRTool.ts (1)

7-9: LGTM! Import path migration to shared aliases.

The import paths have been updated to use the shared @/shared/lib/... aliases, aligning with the PR's goal of migrating to shared path conventions. All imports resolve correctly and follow best practices by importing from specific module files rather than barrel files to maintain clear dependency graphs. The logic remains unchanged.

shared/src/lib/github/pullRequests.ts (1)

1-1: LGTM! Import path migration to shared aliases.

The import paths have been successfully migrated to the shared @/shared/lib/... and @/shared/utils/... aliases. All imported modules and their exports exist at the aliased locations. No runtime behavior changes.

tsconfig.json (1)

45-45: Good practice: excluding the shared package source.

Adding shared/** to the exclude list prevents TypeScript from processing the shared package files directly when they're also accessible via path aliases. This avoids duplicate declarations and potential compilation issues.

shared/src/services/job.ts (1)

3-5: Import migrations are consistent.

All three imports have been updated to use the @/shared/* alias pattern. The addJob function signature and logic remain unchanged.

shared/src/services/queue.ts (1)

3-4: Import path updates are correct and resolve properly.

Both target modules (shared/src/adapters/ioredis/client.ts and shared/src/entities/Queue.ts) exist and contain actual implementations rather than re-exports, conforming to the barrel file guidelines. The imports are valid and consistent with the @/shared/* alias pattern.

__tests__/config/jest.config.base.ts (2)

21-26: Transform configuration is appropriate.

The transform and transformIgnorePatterns configuration correctly handles TypeScript file processing and ES module transformation for the three packages that require it: shared (workspace dependency), @octokit/* (direct dependencies), and universal-user-agent (transitive dependency of @octokit packages).


6-19: Granular module mappings improve clarity.

The refactored moduleNameMapper adds explicit, granular mappings for specific import aliases before the catch-all @/(.*)$ pattern. This order is correct—more specific patterns must precede the catch-all to match correctly.

Verify that all test files resolve imports correctly with the new configuration by running the test suite.

shared/src/lib/github/index.ts (1)

10-11: Import paths are correctly configured.

The updated imports from @/shared/auth and @/shared/lib/types/github are properly resolved by shared/tsconfig.json and the target modules exist. No old import patterns remain in the codebase.

Comment on lines +180 to +184
octokit.rest.repos.getBranch({
owner,
repo,
branch,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Missing await breaks branch existence check.

The API call to octokit.rest.repos.getBranch returns a Promise but is not awaited. This causes the function to immediately return true (line 186) without waiting for the API call to complete, and errors won't be caught by the catch block. The function will always return true regardless of whether the branch exists.

🔎 Proposed fix
-    octokit.rest.repos.getBranch({
+    await octokit.rest.repos.getBranch({
       owner,
       repo,
       branch,
     })
📝 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
octokit.rest.repos.getBranch({
owner,
repo,
branch,
})
await octokit.rest.repos.getBranch({
owner,
repo,
branch,
})
🤖 Prompt for AI Agents
In shared/src/lib/github/content.ts around lines 180 to 184, the call to
octokit.rest.repos.getBranch is not awaited so the function returns true
immediately and exceptions aren't caught; update the code to await the API call
(await octokit.rest.repos.getBranch(...)) and only return true after the awaited
call completes successfully, allowing the existing try/catch to catch errors and
return false on failure.

@vercel vercel bot temporarily deployed to Preview – issue-to-pr-realtime December 30, 2025 01:39 Inactive
@vercel vercel bot temporarily deployed to Preview – issue-to-pr-storybook December 30, 2025 01:39 Inactive
youngchingjui added a commit that referenced this pull request Dec 30, 2025
## Summary
Fix critical command injection vulnerabilities in container execution by adding support for safe array commands and updating vulnerable code patterns.

## Security Impact
**CRITICAL**: The previous implementation allowed command injection through user-controlled input like branch names, potentially leading to:
- Arbitrary code execution in containers (running as root)
- Data exfiltration from Neo4j, Redis, or file system  
- Container escape or privilege escalation

## What Changed

### 🔒 Enhanced Container Execution Security
- **execInContainerWithDockerode** now accepts both strings and arrays
- Array commands bypass shell interpolation completely (injection-proof)
- Added comprehensive security documentation with examples

### 🛠️ Fixed Critical Vulnerability in Branch Tool
**Before (vulnerable)**:
```typescript
await exec(`git checkout ${branch}`)  // If branch = "main; rm -rf /" → RCE!
```

**After (secure)**:
```typescript
await execInContainerWithDockerode({ 
  name: container, 
  command: ["git", "checkout", branch]  // Safe: no shell interpolation
})
```

### 📚 Security Guidelines Added
- Clear documentation of safe vs unsafe patterns
- Examples showing injection attack vectors
- Migration path for existing code

## Test Plan
- [x] All existing docker tests pass
- [x] New tests verify array command functionality  
- [x] Branch tool updated to use secure pattern
- [x] Backwards compatibility maintained for existing string commands

## Migration Recommendation
This PR provides the **foundation for security**. Follow-up PRs should:

1. **Audit all execInContainerWithDockerode calls** for user input
2. **Migrate high-risk patterns** (git operations, file paths, etc.) to array form
3. **Add input validation** for branch names and other user-controlled data

## Context
This addresses the command injection vulnerability identified in CodeRabbit reviews of PR #1403/#1413, where `headRef` from GitHub PR data was being interpolated into shell commands.

🤖 Generated with [Claude Code](https://claude.ai/code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Strengthens container command execution to prevent injection and updates tooling to use safe patterns.
> 
> - `execInContainerWithDockerode` now accepts `string | string[]`; array form executes directly, strings run via `sh -c` for backward compatibility. Added security documentation and examples in JSDoc.
> - Updated `shared/src/lib/tools/Branch.ts` to use array-form git commands for `branch --list`, `checkout -b`, and `checkout -q` inside containers.
> - New tests in `__tests__/lib/docker.test.ts` validate string vs array behavior, proper `Cmd` formation, and error handling for missing/empty container names.
> 
> <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 8072b2c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **Bug Fixes**
  * Enhanced security of container command execution to prevent shell injection vulnerabilities.
  * Improved Git operations reliability in containers through safer command handling.

* **Tests**
  * Added comprehensive test coverage for container command execution.

<sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@vercel vercel bot temporarily deployed to Preview – issue-to-pr-storybook December 30, 2025 02:03 Inactive
@vercel vercel bot temporarily deployed to Preview – issue-to-pr-realtime December 30, 2025 02:03 Inactive
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI generated AI-generated Pull Requests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trigger workflow on PR label creation via webhook

2 participants