Skip to content

ADR-313: Add dev-team:watch-pr post-hand-off PR monitor and /watch-pr command - #84

Merged
jodavis merged 8 commits into
feature/ADR-296-concurrencyfrom
dev/claude/ADR-313
Jul 24, 2026
Merged

ADR-313: Add dev-team:watch-pr post-hand-off PR monitor and /watch-pr command#84
jodavis merged 8 commits into
feature/ADR-296-concurrencyfrom
dev/claude/ADR-313

Conversation

@jodavis-claude

Copy link
Copy Markdown
Collaborator

Work item

ADR-313 — build dev-team:watch-pr, the long-lived, user-started post-hand-off PR monitor
skill, plus /watch-pr, its manual entry point, so that once a task's PR is handed off to a
human reviewer something keeps it in sync (rebasing on a moved base or a merged dependency,
fixing review comments and CI failures) until it merges, without any human polling it by hand.
Depends on ADR-309, ADR-311, ADR-312, and ADR-335, all already merged on
feature/ADR-296-concurrency.

Changes

  • plugins/dev-team/skills/watch-pr/SKILL.md — new. dev-team:watch-pr, the long-lived
    post-hand-off PR monitor (Orchestrator). Runs the worktree-freshness check (copied verbatim
    from ensure-working-branch), resolves the context file, git fetch origin && git checkout <working_branch>, removes the implement-phase worktree/branch and records
    watch_worktree_path/watch_worktree_branch, then loops on watch_pr_poll.py, reacting to
    every fired event (task_merged short-circuits everything else; otherwise rebase-related
    events first via rebase_mechanic.rebase_onto(), then review_comment/ci_failure spawn
    fix-pr). On a rebase conflict, spawns the developer agent (nested, no isolation) to run
    resolve-rebase-conflict, then pushes --force-with-lease on "resolved" or runs
    git rebase --abort and stops itself on "unresolved" (no AskUserQuestion fallback).
  • plugins/dev-team/commands/watch-pr.md — new. Thin /watch-pr <task-key> command (Wrapper),
    same shape as implement.md's single-task dispatch branch: spawns
    dev-team:watch-pr via the Agent tool with isolation: "worktree".
  • plugins/dev-team/skills/concurrent-orchestrate/SKILL.md — modified. Added step 1e: the
    dev-team:watch-pr auto-start hook. Step 1d's existing "any spawned pipeline finishes" trigger
    now also runs 1e, which keeps an in-session (not persisted) record of task_ids already given a
    monitor, and for each newly-finished-successfully task_id, spawns dev-team:watch-pr as a
    local background Agent (isolation: "worktree", run_in_background: true), mirroring step
    1c's own workflow-orchestrate spawn pattern.

watch_pr_poll.py, pr_event_detector.py, rebase_mechanic.py, resolve-rebase-conflict, and
fix-pr are all reused unmodified from ADR-309/ADR-311/ADR-312.

Design decisions

  • concurrent-orchestrate correlates a finished background pipeline to its task_id by keeping
    an in-session record of task_ids already given a monitor, and sanity-checks that task's own
    context file pr_url field before spawning dev-team:watch-pr — no new persisted bookkeeping.
  • A plain single-task /implement <key> run still gets no monitor unless the user runs
    /watch-pr manually afterward — auto-start is attributed solely to concurrent-orchestrate,
    per the spec.
  • Worktree/branch removal uses git worktree remove <path> --force + git branch -D <branch>
    for both the implement-phase removal and the monitor's own self-removal on task_merged.
  • dev-team:watch-pr is spawned via a bare Agent(subagent_type: "claude", isolation: "worktree", ...) call with no dedicated agent persona file, matching every other prose-only
    Orchestrator skill in this repo.
  • Nested fix-pr/rebase-conflict spawns write to distinctly-named sections
    (Post-Handoff Fix <n>, Rebase Conflict <n>, in-session counters) rather than reusing
    dev_team.py's work_summaries-based Fix N numbering, since dev-team:watch-pr never runs
    through that state machine.
  • Self-review fix: event handling now ensures task_merged short-circuits every other event in
    a pass, and review_comment/ci_failure are always handled before returning to the poll, even
    after a rebase-related event's own conflict-resolution detour concludes — otherwise an
    already-fired review/CI event could be silently dropped since pr_event_detector.py marks
    events "seen" the instant they're detected, not when acted on.

Testing completed

No production Python files were touched (watch_pr_poll.py, pr_event_detector.py, and
rebase_mechanic.py are all reused unmodified). This task's three components
(dev-team:watch-pr, /watch-pr, and the concurrent-orchestrate extension) are
Wrapper/Orchestrator-tier agent-skill prose with no existing E2E/Gherkin harness in this repo and
no precedent of one being added for equivalent prior work (ADR-310's concurrent-orchestrate
and its /implement extension were each a single prose-only commit with no test file). Per
missing-test-harness, no new test harness was invented; verification is via the task brief's
own Given/When/Then exit-criteria scenarios plus code review, not new automated coverage.

Orchestrator-tier skill wiring together already-built components (watch_pr_poll.py,
pr_event_detector.py, rebase_mechanic.py from ADR-311/ADR-309, resolve-rebase-conflict
from ADR-312, fix-pr) into the entire post-hand-off lifecycle from hand-off to merge:
worktree-freshness check, checkout, implement-phase worktree removal, then a poll loop
reacting to review comments/CI failures (spawn fix-pr), base moves/dependency merges
(run the rebase mechanic, re-targeting base_branch first for dependency_merged), and
task_merged (remove own worktree, halt). On a rebase conflict, spawns the developer
agent to run resolve-rebase-conflict; resolved -> push --force-with-lease itself;
unresolved -> git rebase --abort then stop the agent (no AskUserQuestion fallback).
Thin Wrapper command, same shape as implement.md's single-task dispatch branch: its whole
job is spawning dev-team:watch-pr via the Agent tool with isolation: "worktree", for when
concurrent-orchestrate's auto-start never happened (e.g. its own session was interrupted
before this task reached hand-off).
…r on hand-off

Step 1d's existing 'any spawned pipeline finishes' trigger now also runs a new step 1e:
for each task_id whose spawned workflow-orchestrate pipeline just finished successfully
(reached hand-off) and that doesn't already have a monitor, spawn dev-team:watch-pr as a
local background Agent, mirroring step 1c's own spawn pattern for workflow-orchestrate
itself. Tracks already-spawned task_ids in an in-session record (not persisted) to avoid
double-spawning. The concurrency cap is untouched -- it only ever counted active
workflow-orchestrate spawns, never this monitor.
…iew)

task_merged now short-circuits every other event in the same pass (nothing else fired
alongside a merge is still actionable against an already-closed PR). Separately,
review_comment/ci_failure are no longer skipped when a rebase-related event in the same
pass also needs the rebase mechanic (or a conflict resolution) first -- pr_event_detector.py
marks those events 'seen' the instant it detects them, so deferring them to the next poll
would have silently dropped them instead. They're now always handled before returning to
the poll, after the rebase-related work (including any conflict resolution) concludes.
@github-actions

Copy link
Copy Markdown

build-and-test: Python test results

Status: ✅ Passed

Test log

@jodavis-claude jodavis-claude left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed against the ADR-313 task brief's exit criteria and _spec_ConcurrentDevelopment.md. The three components (dev-team:watch-pr, /watch-pr, the concurrent-orchestrate auto-start hook) implement all listed exit criteria and match the spec's Data Flow/Interfaces sections closely — spawn shapes mirror FixPrStep's and workflow-orchestrate's existing patterns, rebase_onto()/watch_pr_poll.py/pr_event_detector.py are called with correct signatures and left unmodified, and the event-ordering logic (task_merged short-circuit, dependency_merged re-target before base_updated, review_comment/ci_failure always handled before returning to poll) is sound. CI passes; diff is scoped to exactly the three files described. No production Python was touched, consistent with the brief's "no unit tests" note.

A few fault-tolerance gaps worth tightening before merge (see inline comments), plus one low-priority doc-consistency nit.

Comment thread plugins/dev-team/skills/watch-pr/SKILL.md
Comment thread plugins/dev-team/skills/watch-pr/SKILL.md
Comment thread plugins/dev-team/skills/concurrent-orchestrate/SKILL.md
Comment thread plugins/dev-team/skills/watch-pr/SKILL.md Outdated
…e/branch removal fails

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:113 - the removal
commands had no stated failure behavior, unlike every other command call in this skill.
…moval fails on task_merged

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:159 - the success
report was unconditional even though it followed a removal that could fail, risking a failed
cleanup being reported as a clean halt.
…d by both call sites

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:10 - argument-hint
previously read <work-item-id> instead of the flag style both commands/watch-pr.md and
concurrent-orchestrate/SKILL.md actually use to invoke this skill.
… is empty

Addresses PR review comment on plugins/dev-team/skills/concurrent-orchestrate/SKILL.md:143 -
the pr_url sanity check had no defined behavior on failure, unlike this spec's consistent
never-silently-proceed posture elsewhere. Now skips spawning the monitor for that task_id,
reports the inconsistency in detail, and still records the task_id so a later poll doesn't
repeatedly re-report it.

@jodavis-claude jodavis-claude left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sign-off review: all 4 prior review threads verified resolved.

  1. watch-pr SKILL.md:113 (step 3 worktree/branch removal) - hard-stop language added, matches convention. Resolved.
  2. watch-pr SKILL.md:161 (step 4b bullet 0 own-worktree removal on task_merged) - success report now conditional on both removal commands succeeding; non-zero exit is now a hard stop. Resolved.
  3. concurrent-orchestrate SKILL.md:143 (empty pr_url fallback) - explicit skip-and-report-and-record-anyway behavior added. Resolved.
  4. watch-pr SKILL.md:10 (argument-hint style) - now --work-item-id <work-item-id>, matching both call sites. Resolved.

Scanned both modified files (watch-pr/SKILL.md, concurrent-orchestrate/SKILL.md) in full for new issues introduced by the fix commits (734a5b5, b344a80, ee1bc99, 22c758c) - none found. No production Python was touched by this fix pass, consistent with the developer's summary. Approving sign-off.

@jodavis-claude
jodavis-claude marked this pull request as ready for review July 23, 2026 15:07
@jodavis-claude
jodavis-claude requested a review from jodavis July 23, 2026 15:07
Comment thread plugins/dev-team/commands/watch-pr.md
Comment thread plugins/dev-team/skills/watch-pr/SKILL.md
Comment thread plugins/dev-team/skills/watch-pr/SKILL.md
Comment thread plugins/dev-team/skills/watch-pr/SKILL.md
@jodavis
jodavis merged commit 475d743 into feature/ADR-296-concurrency Jul 24, 2026
3 checks passed
@jodavis
jodavis deleted the dev/claude/ADR-313 branch July 24, 2026 14:20
jodavis pushed a commit that referenced this pull request Jul 24, 2026
* ADR-313: add dev-team:watch-pr, the long-lived post-hand-off PR monitor

Orchestrator-tier skill wiring together already-built components (watch_pr_poll.py,
pr_event_detector.py, rebase_mechanic.py from ADR-311/ADR-309, resolve-rebase-conflict
from ADR-312, fix-pr) into the entire post-hand-off lifecycle from hand-off to merge:
worktree-freshness check, checkout, implement-phase worktree removal, then a poll loop
reacting to review comments/CI failures (spawn fix-pr), base moves/dependency merges
(run the rebase mechanic, re-targeting base_branch first for dependency_merged), and
task_merged (remove own worktree, halt). On a rebase conflict, spawns the developer
agent to run resolve-rebase-conflict; resolved -> push --force-with-lease itself;
unresolved -> git rebase --abort then stop the agent (no AskUserQuestion fallback).

* ADR-313: add /watch-pr manual fallback command

Thin Wrapper command, same shape as implement.md's single-task dispatch branch: its whole
job is spawning dev-team:watch-pr via the Agent tool with isolation: "worktree", for when
concurrent-orchestrate's auto-start never happened (e.g. its own session was interrupted
before this task reached hand-off).

* ADR-313: extend concurrent-orchestrate to auto-start dev-team:watch-pr on hand-off

Step 1d's existing 'any spawned pipeline finishes' trigger now also runs a new step 1e:
for each task_id whose spawned workflow-orchestrate pipeline just finished successfully
(reached hand-off) and that doesn't already have a monitor, spawn dev-team:watch-pr as a
local background Agent, mirroring step 1c's own spawn pattern for workflow-orchestrate
itself. Tracks already-spawned task_ids in an in-session record (not persisted) to avoid
double-spawning. The concurrency cap is untouched -- it only ever counted active
workflow-orchestrate spawns, never this monitor.

* ADR-313: fix event-ordering bug in watch-pr's poll reaction (self-review)

task_merged now short-circuits every other event in the same pass (nothing else fired
alongside a merge is still actionable against an already-closed PR). Separately,
review_comment/ci_failure are no longer skipped when a rebase-related event in the same
pass also needs the rebase mechanic (or a conflict resolution) first -- pr_event_detector.py
marks those events 'seen' the instant it detects them, so deferring them to the next poll
would have silently dropped them instead. They're now always handled before returning to
the poll, after the rebase-related work (including any conflict resolution) concludes.

* ADR-313: watch-pr step 3 - stop and report if implement-phase worktree/branch removal fails

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:113 - the removal
commands had no stated failure behavior, unlike every other command call in this skill.

* ADR-313: watch-pr step 4b - stop and report if own worktree/branch removal fails on task_merged

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:159 - the success
report was unconditional even though it followed a removal that could fail, risking a failed
cleanup being reported as a clean halt.

* ADR-313: watch-pr argument-hint - match --work-item-id flag style used by both call sites

Addresses PR review comment on plugins/dev-team/skills/watch-pr/SKILL.md:10 - argument-hint
previously read <work-item-id> instead of the flag style both commands/watch-pr.md and
concurrent-orchestrate/SKILL.md actually use to invoke this skill.

* ADR-313: concurrent-orchestrate step 1e - define fallback when pr_url is empty

Addresses PR review comment on plugins/dev-team/skills/concurrent-orchestrate/SKILL.md:143 -
the pr_url sanity check had no defined behavior on failure, unlike this spec's consistent
never-silently-proceed posture elsewhere. Now skips spawning the monitor for that task_id,
reports the inconsistency in detail, and still records the task_id so a later poll doesn't
repeatedly re-report it.

* ADR-313: watch-pr - "already merged" bullet applies regardless of prior run

Addresses PR #84 review comment: whether or not there was a prior run of
this skill, if the task's PR already merged there is nothing to do.

* ADR-313: watch-pr - extract inlined rebase-mechanic invocation into a script

Addresses PR #84 review comment: step 4c's inline `python -c "..."` block
is replaced by a new CLI wrapper script, run_rebase_mechanic.py, alongside
rebase_mechanic.py in workflow-orchestrate/scripts/, with unit test
coverage for its success and error paths (rebase_onto itself is already
covered by test_rebase_mechanic.py).

* ADR-313: watch-pr - remove Todo list monitoring/mirroring support

Addresses PR #84 review comment: removed step 2's Monitor()+TodoWrite
mirroring of this session's own todo log (unreliable, complicated, and
not useful for an unattended background agent). Kept computing the todo
log path itself, since nested fix-pr/resolve-rebase-conflict spawns
(step 4b/5) still pass it through as workflow-worker's required
--todo-log argument; removing that mediated-spawn pattern entirely is
tracked separately as ADR-314 and not pre-empted here.

* ADR-313: watch-pr - document why a subagent is spawned and how it communicates

Addresses PR #84 review comment questioning the subagent spawn in
commands/watch-pr.md: confirmed this is intentional (unlike /implement's
single-task path, dev-team:watch-pr must own a guaranteed-fresh worktree
and blocks for the task's whole hand-off-to-merge lifecycle, so it cannot
run inline in the invoking session). Added a "Communicating outward"
section to watch-pr/SKILL.md making explicit the two channels this skill
already relies on for anything it needs to convey: fix-pr's PR-thread
replies for review/CI issues, and a detailed final message on any
hard-stop condition, surfaced via the harness's background-task
notification and resumable via SendMessage. commands/watch-pr.md now
references this section and explains the isolation/blocking rationale
for the spawn.

* ADR-314: remove todo-log/TodoWrite-mirroring plumbing

Removes the todo-log-tailing / TodoWrite-mirroring mechanism from
workflow-orchestrate and workflow-worker: it never surfaced well even for a
single task's pipeline, and had no clean extension to the concurrent
multi-task orchestrator this spec introduces.

- workflow-orchestrate/SKILL.md: drop the todo-log-path step and the
  Monitor/TaskStop-based tailing step, renumber remaining steps, and drop
  --todo-log from the spawn_agent prompt template.
- Delete workflow-orchestrate/scripts/get_todo_log_path.py (no dedicated
  test file existed).
- workflow-worker/SKILL.md: drop the log-redirection step and --todo-log
  argument, renumber remaining steps.
- Delete workflow-worker/scripts/append_todo_log.py (no dedicated test
  file existed).
- developer.md, researcher.md, reviewer.md: remove the "Task tracking"
  instruction to mirror updates via TodoWrite through the log file. The
  TodoWrite tool grant in each file's frontmatter is left in place since
  the exit criteria scope only the instruction text.
- watch-pr/SKILL.md: this ADR-313-added skill still depended on the exact
  plumbing being removed here (its own text explicitly deferred this
  cleanup to ADR-314); updated to drop get_todo_log_path.py from its
  script list, remove its own todo-log-path computation step, and drop
  --todo-log from both nested workflow-worker spawn prompts.

Verified via repo-wide grep that no todo_log/TaskStop/--todo-log/
get_todo_log_path/append_todo_log references remain, and the existing
workflow-orchestrate test suite (test_dev_team.py, 111 tests) still
passes unchanged.

---------

Co-authored-by: Claude on behlaf of jodavis <ElwoodMoves@hotmail.com>
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.

3 participants