Skip to content

AR-273: advance factory issues after PR merge#361

Closed
khaliqgant wants to merge 1 commit into
mainfrom
ar-273-post-merge-advance
Closed

AR-273: advance factory issues after PR merge#361
khaliqgant wants to merge 1 commit into
mainfrom
ar-273-post-merge-advance

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 15, 2026

Copy link
Copy Markdown
Member

Summary

  • handle merged PR metadata events even when the PR babysitter is disabled or already released
  • advance matching Agent Implementing / In Human Review Linear issues to Done when their PR is merged
  • force in-flight merged-PR completion to Done without re-running the merge gate
  • allow Human Review -> Ready for Agent to reset terminal dispatch state for operator re-dispatch

Tests

  • /Users/khaliqgant/Projects/AgentWorkforce/pear/node_modules/.bin/vitest run packages/factory-sdk/src/orchestrator/factory.test.ts packages/factory-sdk/src/config/schema.test.ts
  • /Users/khaliqgant/Projects/AgentWorkforce/pear/node_modules/.bin/tsc --noEmit -p packages/factory-sdk/tsconfig.json

Notes

  • This PR keeps the pear#359 config/schema shape intact: stateIds.humanReview remains operator-configured rather than baked into LINEAR_STATE_IDS.
  • Real Linear activity-log capture was not run from this local implementation branch; regression coverage now exercises PR merged-before-ready, persisted Human Review -> Done, duplicate merged PR event suppression, and Human Review -> Ready re-dispatch.

Review in cubic

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 21 minutes. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9978b1d3-77e9-4df5-9c61-094036c9245b

📥 Commits

Reviewing files that changed from the base of the PR and between 14909ab and 26a421b.

📒 Files selected for processing (2)
  • packages/factory-sdk/src/orchestrator/factory.test.ts
  • packages/factory-sdk/src/orchestrator/factory.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ar-273-post-merge-advance

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.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces automatic advancement of Linear issues to the 'Done' state when their associated GitHub PR is merged, along with duplicate event suppression and support for re-dispatching issues moved back from 'Human Review' to 'Ready for Agent'. The review feedback highlights three important issues: a bug where newly ingested issues are incorrectly flagged as reopened when the human review state is unconfigured, a performance bottleneck where all issue files are read from disk during PR merge processing, and a potential memory leak from an unbounded cache set.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +1522 to +1523
const reopenedFromTerminal = previousStateId === this.#config.stateIds.done ||
previousStateId === this.#config.stateIds.humanReview

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

When an issue is ingested for the first time, previousStateId is undefined. If this.#config.stateIds.humanReview is also undefined (which is common if the operator has not configured it), the comparison previousStateId === this.#config.stateIds.humanReview will evaluate to true. This incorrectly flags the issue as reopenedFromTerminal and resets the dispatch attempts. Adding a guard to ensure previousStateId !== undefined prevents this bug.

    const reopenedFromTerminal = previousStateId !== undefined &&
      (previousStateId === this.#config.stateIds.done ||
       previousStateId === this.#config.stateIds.humanReview)

Comment on lines +2453 to +2467
for (const path of await this.#mount.listTree(ISSUE_ROOT)) {
if (!isIssueFilePath(path)) {
continue
}
const issue = await this.#readIssue(path)
if (!issue || !upstreamStates.has(issue.stateId)) {
continue
}
if (!isRealLinearIssue(issue) || !isInFactoryScope(issue, this.#config.safety)) {
continue
}
if (prSnapshotReferencesIssue(snapshot, issue.key)) {
return issue
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Currently, #findMergeAdvanceIssueForPr reads every single issue file in the repository (await this.#readIssue(path)) to check if it matches the PR snapshot. In repositories with many issues, this O(N) disk/mount I/O operation on every PR merge event will cause significant latency and block the event loop.

Since keyFromPath(path) is a lightweight string operation, we can check if the PR snapshot references the issue key before reading the file. This optimizes the search to O(1) file reads.

    for (const path of await this.#mount.listTree(ISSUE_ROOT)) {
      if (!isIssueFilePath(path)) {
        continue
      }
      const issueKey = keyFromPath(path)
      if (!prSnapshotReferencesIssue(snapshot, issueKey)) {
        continue
      }
      const issue = await this.#readIssue(path)
      if (!issue || !upstreamStates.has(issue.stateId)) {
        continue
      }
      if (!isRealLinearIssue(issue) || !isInFactoryScope(issue, this.#config.safety)) {
        continue
      }
      return issue
    }

this.#increment('mergedPrAdvanceDuplicatesSuppressed')
return
}
this.#postMergeDoneAdvances.add(advanceKey)

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

The #postMergeDoneAdvances Set is used to suppress duplicate merge events, but it grows indefinitely as the long-running daemon processes more PRs over time. To prevent a slow memory leak, we should bound the Set's size by evicting the oldest entry when it exceeds a reasonable threshold.

    this.#postMergeDoneAdvances.add(advanceKey)
    if (this.#postMergeDoneAdvances.size > 5000) {
      const oldest = this.#postMergeDoneAdvances.values().next().value
      if (oldest !== undefined) {
        this.#postMergeDoneAdvances.delete(oldest)
      }
    }

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26a421beb2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

this.#postMergeDoneAdvances.add(advanceKey)

try {
await this.#linear.setState(issue, this.#config.stateIds.done)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Complete matched in-flight issues through the batch

When a merged PR's branch name does not contain the issue key but its title/body does, record is undefined even though #findMergeAdvanceIssueForPr can still find the Agent Implementing issue from the mount. This direct setState marks Linear Done without going through #completeIssue, so the implementer/reviewer are not released and the batch slot remains in-flight. Please check the matched issue against #batch and complete that record before taking the direct persisted-state path.

Useful? React with 👍 / 👎.

if (!isRealLinearIssue(issue) || !isInFactoryScope(issue, this.#config.safety)) {
continue
}
if (prSnapshotReferencesIssue(snapshot, issue.key)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restrict merged PR advancement to configured repos

Because the live/default subscription watches all /github/repos/** PR paths, this content-only issue-key match can advance a Human Review or Agent Implementing Linear issue from a merged PR in an unrelated mounted repository that merely mentions the same key. The existing PR resolvers limit candidates to configured repos; carry the repo from githubPullPathParts into this lookup and reject repos outside the factory route/config before accepting the match.

Useful? React with 👍 / 👎.

@khaliqgant

Copy link
Copy Markdown
Member Author

Superseded by #362, which includes the post-merge advancement path plus the config-driven terminal-state completion path. Closing this narrower duplicate so AR-273 review can focus on #362.

@khaliqgant

Copy link
Copy Markdown
Member Author

Closing as superseded by #362.

@khaliqgant khaliqgant closed this Jun 15, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

Findings

High: packages/factory-sdk/src/orchestrator/factory.ts can treat first-seen issues as reopened from Human Review when stateIds.humanReview is unset, because both sides of previousStateId === this.#config.stateIds.humanReview can be undefined. If a terminal dispatch record survives while canonical state memory is empty, a Ready issue can incorrectly reset dispatch terminal state. I did not edit this because it touches dispatch guard/default behavior, which is safety-critical under your instructions. Suggested human patch: require previousStateId !== undefined before comparing terminal states.

Addressed comments

  • CodeRabbit: review was rate-limited and did not raise code feedback. No code change needed.
  • Gemini Code Assist: first-seen previousStateId/humanReview undefined bug at packages/factory-sdk/src/orchestrator/factory.ts:1522. Valid; not changed because it changes dispatch terminal-state logic and guard behavior.
  • Gemini Code Assist: #findMergeAdvanceIssueForPr reads all issue files before filtering by issue key at packages/factory-sdk/src/orchestrator/factory.ts:2457. Valid performance concern; not changed because reordering lookup/read/safety checks is semantic behavior, not mechanical cleanup.
  • Gemini Code Assist: #postMergeDoneAdvances can grow unbounded at packages/factory-sdk/src/orchestrator/factory.ts:2416. Valid long-running daemon concern; not changed because choosing eviction policy changes duplicate-suppression behavior.
  • Gemini Code Assist review summary: duplicates the three inline findings above; no separate action.

No files were edited.

Verification

Passed locally: npm ci, npm run verify:mcp-resources-drift, npm run lint (warnings only), npm run typecheck:web, npm run typecheck:node, npm test (122 passed), npx vitest run (835 passed), npm run build, npm run build:web, npx playwright test --config playwright.fidelity.config.ts (8 passed), and npx playwright test --config playwright.redraw.config.ts (1 passed). The first Playwright attempt failed only because Chromium was missing; after npx playwright install chromium, the rerun passed.

Remote GitHub check completion was not verified, and I did not run the macOS packaging smoke job in this Linux sandbox.

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