Skip to content

fix(orchestrator): detect '# Plan' heading to prevent duplicate in PR body#9

Open
Maxtanh-Meta wants to merge 11 commits into
logosc:mainfrom
Maxtanh-Meta:fix/plan-heading-h1-duplicate
Open

fix(orchestrator): detect '# Plan' heading to prevent duplicate in PR body#9
Maxtanh-Meta wants to merge 11 commits into
logosc:mainfrom
Maxtanh-Meta:fix/plan-heading-h1-duplicate

Conversation

@Maxtanh-Meta

Copy link
Copy Markdown

Summary

Fixes the duplicate plan heading bug seen in XF-APAC/one-minute-games#54.

The agent sometimes outputs # Plan (H1) instead of ## Plan (H2). The previous fix (PR #3, commit 647d3fb) only checked for the ## Plan prefix, so an H1 heading still caused buildPRBody to prepend its own ## Plan, producing:

## Plan

# Plan

## Issue Context...

Fix

Replace the simple strings.HasPrefix(planText, "## Plan") check with a hasPlanHeading() helper that detects plan headings at any markdown level (#, ##, ###, etc.). The helper:

  1. Extracts the first line
  2. Checks it starts with #+ characters followed by a space
  3. Checks the heading word is Plan (case-insensitive)

Test

Added a test case for the # Plan (H1) variant to the existing TestBuildPRBodyNoDuplicatePlanHeading test.

maxtanh and others added 10 commits May 13, 2026 20:43
Previously, the reviewer only posted a comment on rejection. This makes
the approval path equally visible — operators and issue authors can see
why the reviewer approved and that the gate was evaluated, without
needing to check orchestrator logs.
After a PR is created, run the configured reviewer agent on the actual
code diff (not just the plan). The reviewer evaluates the implementation
for bugs, style issues, missing error handling, test coverage gaps, and
security concerns, then posts its findings as a comment on the PR.

This is a best-effort step — if the reviewer fails or is not configured,
the PR is still created and labeled normally. The code review is
informational and does not block the PR.

The reviewer reuses the existing reviewer infrastructure (same provider,
same timeout config) but with a code-review-specific prompt that
includes the full diff against the base branch.
buildPRBody unconditionally prepended '## Plan' before job.PlanText,
but the planning agent often includes its own '## Plan' heading in the
markdown output. This resulted in duplicate headings in PR descriptions.

Now checks if PlanText already starts with '## Plan' before adding one.
Without this fetch, when multiple jobs run sequentially and earlier PRs
get merged between runs, origin/<base> becomes stale. The three-dot diff
(origin/main...HEAD) then includes commits from previously-merged PRs,
causing the reviewer to flag unrelated changes.

Observed on XF-APAC/one-minute-games PR #50: the code review saw README
changes from PR #49 (issue #38) because origin/main hadn't been updated
since that PR was merged.
… review

When a human submits a CHANGES_REQUESTED review on a symphony-go PR,
the orchestrator now detects it via polling and runs a single revision
cycle on the existing branch/worktree:

1. PollPRRevisions (new tick step) scans pr_ready jobs for fresh
   CHANGES_REQUESTED reviews submitted after the job's UpdatedAt.
2. runPRRevision drives the implementation agent with a revision prompt
   containing the original issue, approved plan, and review feedback.
3. The agent's changes are committed and pushed to the same branch,
   updating the PR in place (no close/reopen cycle).
4. Job.RevisionAttempted is set to true — only one revision cycle runs
   per PR, subsequent reviews are left for human follow-up.

Changes:
- internal/github: add PRReview type, ListPRReviews to Client interface
- internal/github/fake: add SeedPRReview + ListPRReviews
- internal/types: add RevisionAttempted field to Job
- internal/orchestrator/loop: call PollPRRevisions in tick
- internal/orchestrator/revision.go (new): polling + execution logic
- internal/orchestrator/revision_test.go (new): 3 test cases
fix(orchestrator): deduplicate '## Plan' heading in PR body
feat: add post-PR code review using the reviewer agent
feat: post issue comment when reviewer approves a plan
feat(orchestrator): add single-cycle PR revision on CHANGES_REQUESTED review
@Maxtanh-Meta

Copy link
Copy Markdown
Author

Code Review (via Codex)

Overall: Approve with suggestions

Real Issue

hasPlanHeading has a dead branch (job.go:1245)

word := strings.TrimSpace(stripped)
return strings.EqualFold(word, "Plan") || strings.HasPrefix(strings.ToLower(word), "plan\n") || strings.HasPrefix(strings.ToLower(word), "plan ")

word comes from a single line (sliced at \n) and is then TrimSpaced, so "plan\n" can never match — dead branch. The "plan " branch also can't match when the heading is exactly # Plan with no trailing text (since TrimSpace strips the trailing space too, leaving just "plan" which only hits the EqualFold branch).

Suggested simplification:

lower := strings.ToLower(word)
return lower == "plan" || strings.HasPrefix(lower, "plan ")

This correctly handles # Plan, ## Plan, ## Plan: details, and ## Plan - X while rejecting ## Planning or ## Planet.

Minor Notes

  • The existing runPostPRCodeReview (from PR feat: add post-PR code review using the reviewer agent #5, not merged yet) uses workspace.LayoutFor instead of layoutForJob — rename-unsafe. runPRRevision in this PR's sibling already gets it right.
  • At scale, PollPRRevisions calling ListPRReviews per pr_ready job each tick will hit GitHub rate limits — fine for now, worth a TODO.
  • RevisionAttempted=true is set even on agent error paths, so a transient failure permanently disables revision on that PR. Intentional (one-cycle design) but worth flagging for the team.

Verdict

The fix is correct and the test coverage is good. The dead branch in hasPlanHeading should be cleaned up but it doesn't cause incorrect behavior — the other branches cover the real cases.

… body

The agent sometimes outputs '# Plan' (H1) instead of '## Plan' (H2).
The previous fix (PR logosc#3) only checked for '## Plan' prefix, so an H1
heading still caused buildPRBody to prepend its own '## Plan', resulting
in duplicate headings like:

    ## Plan

    # Plan

    ## Issue Context...

Replace the simple HasPrefix check with a hasPlanHeading() helper that
detects plan headings at any markdown level (#, ##, ###, etc.).

Fixes: https://github.com/XF-APAC/one-minute-games/pull/54
@Maxtanh-Meta Maxtanh-Meta force-pushed the fix/plan-heading-h1-duplicate branch from 7533440 to 43e5270 Compare May 18, 2026 06:11
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