| name | mcs-lab-fix-pr-filer |
| description | After mcs-lab-issue-filer has filed (or commented on) the audit issue for a lab, apply the findings' suggested_correction diffs to the lab markdown and copy any proposed_screenshot_replacement images into labs/<slug>/images/. If an OPEN fix-PR for this slug already exists (same author, mergeable), append the commit to it; otherwise open a NEW PR on a run-unique branch `{branch_prefix}/fix-<slug>-content-audit-<run-id>`. Title `<slug>: fix audit findings from #<issue-number>`, body `Closes #<issue-number>`. Dedup is scoped to OPEN PRs only — a merged or closed prior PR never blocks a new one. Should NOT be invoked directly by the user — the orchestrator calls it after mcs-lab-issue-filer returns the issue number.
|
| allowed-tools | ["Read","Write","Edit","Bash(git status:*)","Bash(git checkout:*)","Bash(git branch:*)","Bash(git fetch:*)","Bash(git pull:*)","Bash(git add:*)","Bash(git commit:*)","Bash(git push:*)","Bash(git log:*)","Bash(git diff:*)","Bash(gh pr list:*)","Bash(gh pr view:*)","Bash(gh pr checkout:*)","Bash(gh pr create:*)","Bash(gh pr edit:*)","Bash(gh pr comment:*)","Bash(gh api:*)","Bash(cp:*)"] |
mcs-lab-fix-pr-filer (sub-skill)
Apply the audit's suggested corrections to the actual lab files in the active instance's repo ({repo}), then either open a new fix-PR on a run-unique branch or append to the lab's existing open fix-PR — whichever the dedup check resolves — and close (or reference) the audit issue.
PR dedup is scoped to OPEN PRs only. A merged or closed prior PR for the same lab never blocks a new one — each audit run that finds new problems gets its own PR. The only thing that suppresses a new PR is an already-open fix-PR for the same slug, in which case this run's fixes are appended to that PR instead of opening a duplicate. See [[feedback_fresh_branch_per_pr]].
Inputs (assumed present from the orchestrator)
runs/<run-id>/labs/<slug>/findings.json — same file the issue-filer read.
runs/<run-id>/labs/<slug>/screenshots/proposed/ — proposed replacement images keyed to findings[*].proposed_screenshot_replacement_for paths.
<plugin>/config/judge-config.yml — branch conventions:
issues.pr_append.pr_branch_pattern (default {branch_prefix}/fix-{slug}-content-audit-{run_id}) — the run-unique branch name to CREATE when no open PR exists.
issues.pr_append.pr_match_head_prefix (default {branch_prefix}/fix-{slug}-content-audit) — the head-ref prefix used to find an existing open PR for the slug regardless of its run-id suffix.
runs/<run-id>/existing-state.yml — the orchestrator's Phase 1.4 probe. labs[<slug>].open_pr is non-null when an open fix-PR already exists for the slug.
- the resolved clone of
{repo} (from Resolve-LabRepo.ps1 -Instance).
issue_number — returned by mcs-lab-issue-filer. Required.
lab_slug, run_id — passed through.
Outputs
- Either a new run-unique branch
{branch_prefix}/fix-<slug>-content-audit-<run-id> on the active instance's repo ({repo}), OR a new commit on the existing open PR's branch — never both.
- New or updated PR titled
<slug>: fix audit findings from #<issue-number> with body containing Closes #<issue-number>.
runs/<run-id>/labs/<slug>/pr-url.txt — convenience file for the orchestrator's summary.
- Updated
manifest.yml.labs[<slug>]: status: issue_and_pr_filed, pr_url, pr_action: created | appended | skipped_no_changes.
Procedure
1. Prepare the working tree
cd C:\Users\dewainr\mcs-labs
git fetch origin main
If there are uncommitted changes in the working tree, stash them with a clear label and restore the user's prior HEAD. (Never clobber unrelated in-flight work.)
2. Resolve the PR target — append to an open PR, else open a new one
Dedup is scoped to OPEN PRs only. First find whether an open fix-PR already exists for this slug; if so, append to it; otherwise open a brand-new PR on a run-unique branch. A merged or closed prior PR is irrelevant — it never blocks a new PR.
2a. Find an existing open PR for the slug.
- If the orchestrator's probe wrote
runs/<run-id>/existing-state.yml.labs[<slug>].open_pr (non-null), use it directly — it already carries number, branch (the actual head ref, run-id suffix and all), author, and mergeable.
- Otherwise, query directly by head prefix (matches any run's branch for this slug), most-recently-updated first:
prefix="{branch_prefix}/fix-<slug>-content-audit"
gh pr list --repo {repo} --state open --author "@me" \
--json number,headRefName,author,mergeable,url,updatedAt \
--search "head:$prefix" | jq 'sort_by(.updatedAt) | reverse | .[0] // empty'
If the head query returns nothing, fall back to a title match: --search "\"<slug>: fix audit findings\" in:title". Only OPEN PRs count.
2b. If an open PR was found → append.
- Verify it's authored by the current
gh user (pr_append.require_same_author) and mergeable (pr_append.require_mergeable).
- If guardrails pass:
git fetch origin, then gh pr checkout <number> --repo {repo} (checks out the PR's actual branch whatever its name). Confirm with git rev-parse --abbrev-ref HEAD. Set pr_action: appended and remember <branch> = that head ref.
- If guardrails fail (different author, or
CONFLICTING): abort the PR step (the orchestrator records this as a soft failure: issue was filed but the open PR could not be appended; the user is told to inspect the PR). Do NOT open a competing second PR for the same lab.
2c. If no open PR exists → open a new one on a run-unique branch.
- Render the create pattern:
branch = {branch_prefix}/fix-<slug>-content-audit-<run-id> (from judge-config.yml.issues.pr_append.pr_branch_pattern, substituting {branch_prefix}, {slug}, and {run_id}). The {run_id} suffix guarantees the name can't collide with a leftover branch from a merged/closed prior PR — so there is no need to rename or delete any stale branch.
git fetch origin main, then git checkout -b <branch> origin/main. Set pr_action: created.
3. Apply markdown corrections
For each finding in findings.json where outcome ∈ {broken, unclear} AND suggested_correction is non-null:
- Read the lab file
_labs/<slug>.md.
- Apply a literal string replacement:
old_string = suggested_correction.original_text
new_string = suggested_correction.proposed_text
- If the
old_string is no longer present (the lab has changed since the audit), record {finding_id, applied: false, reason: original_text_not_found} in runs/<run-id>/labs/<slug>/pr-apply-log.json and continue with the next finding.
- If the replacement would match in multiple places, prefer the location with the line number closest to
finding.evidence.line_number if recorded; otherwise apply once at the first match and warn.
Some corrections span multiple lines or require reordering content (new UC, scene swap, etc.). Findings whose suggested_correction.scope == "scene" should NOT be auto-applied — leave them for human review and record {finding_id, applied: false, reason: scope_scene_requires_manual}.
4. Apply screenshot replacements / additions
For each finding with non-null proposed_screenshot_replacement:
- Resolve source path:
runs/<run-id>/labs/<slug>/screenshots/proposed/<proposed_screenshot_replacement> (the audit subagents save replacements here).
- Resolve destination:
- If
proposed_screenshot_replacement_for is non-null (replacing an existing image): destination is labs/<slug>/<proposed_screenshot_replacement_for> (e.g. labs/<slug>/images/foo.png).
- If null (adding a new image): destination is
labs/<slug>/images/<proposed_screenshot_replacement> and ensure the lab markdown is updated to reference it (the markdown reference should be in the same finding's suggested_correction.proposed_text if the parser caught it).
cp <source> <destination>.
Track screenshots_replaced count for the commit message.
4a. Detect missing screenshot replacements for UI-drift findings
Before moving on to step 5, scan applied findings for the case where the audit caught UI drift (a renamed control, moved button, re-organized dialog, changed label) but the finding does NOT have a proposed_screenshot_replacement populated. This is exactly the case where the lab's screenshot will be stale even after the markdown fix lands — the body text says the new label, the picture still shows the old one. See mcs-lab-auditor/references/playwright-cookbook.md → "Screenshot freshness" for the standing rule.
For each such finding, before committing:
- Identify any image-ref in the same scene as the relabeled instruction line. Most labs put
 one or two paragraphs after the instruction it depicts. Look for image references within the _labs/<slug>.md section being edited.
- If an image-ref exists within the affected scene and the file at
labs/<slug>/images/<file>.png shows the old UI state (i.e. the same surface the audit just observed has been relabeled), the screenshot is stale.
- The audit driver should already have captured a fresh screenshot for this surface during the run — look for it in
runs/<run-id>/labs/<slug>/screenshots/. If found, copy it over labs/<slug>/images/<file>.png.
- If no fresh screenshot was captured (the driver missed the image-ref → instruction mapping during the run), open a separate audit-loop iteration to capture one before proceeding. Do not skip — record
{finding_id, screenshot_refresh: pending_capture, image_path} in pr-apply-log.json and request the screenshot from the orchestrator before commit.
- If the maintainer's fix PR explicitly defers the image refresh (e.g. an explicit "deferred to follow-up" annotation in the finding), record the deferral in the PR body's Test plan under an unchecked checkbox:
- [ ] Refresh \images/.png` to show the current UI`. Never silently ship the markdown-only fix.
Track screenshots_implied_stale and screenshots_refreshed_from_audit_run counts for the commit message and PR body. If screenshots_implied_stale > screenshots_refreshed_from_audit_run + screenshots_deferred, the gap is a process failure — log it loudly so the orchestrator can flag it on the next run.
5. Sanity check
git diff --stat
If the working tree has NO changes after steps 3 and 4 (every correction failed to apply, every screenshot was already current), set pr_action: skipped_no_changes and abort the PR step. Record the reason in pr-apply-log.json.
6. Commit
git add _labs/<slug>.md labs/<slug>/images/
git commit -m "$(cat <<EOF
<slug>: fix audit findings from #<issue-number>
Auto-applied by mcs-lab-fix-pr-filer from audit run <run-id>.
Markdown corrections: <N applied> of <N total> findings.
Screenshot updates: <N replaced> + <N added>.
See issue #<issue-number> for full audit details and per-finding evidence.
Closes #<issue-number>
EOF
)"
Commit author: use the current gh auth status user (e.g. dewainr@microsoft.com / Dewain Robinson) via git -c user.email=... -c user.name=.... Never sign with a Claude / bot identity unless explicitly configured.
7. Push
git push -u origin <branch>
If push fails because the remote branch already exists and is ahead (concurrent run), git pull --rebase origin <branch> then re-push.
8. Open or update the PR
If pr_action: created:
gh pr create \
--repo {repo} \
--base main \
--head <branch> \
--title "<slug>: fix audit findings from #<issue-number>" \
--body-file <pr-body.md>
PR body template (runs/<run-id>/labs/<slug>/pr-body.md):
Closes #<issue-number>.
Implements the corrections from the mcs-lab-auditor run on `<slug>` (run id `<run-id>`).
## What changed
<for each applied finding: a 1-2 line bullet describing the fix>
## How it was verified
Every change was driven by the audit run's Playwright execution on a fresh workshop-issued tenant account. Per-finding evidence (screenshots, accessibility snapshots, console + network logs) is stored locally under `~/.claude/plugins/mcs-lab-auditor/runtime/runs/<run-id>/labs/<slug>/`.
<if any findings were NOT auto-applied:>
## Findings deferred to human review
<for each unapplied finding: a bullet with the reason from pr-apply-log.json>
If pr_action: appended:
- Just push (already done in step 7). Then
gh pr comment <pr-number> --body "Appended fixes from audit run <run-id>: <N> markdown corrections, <N> screenshot updates.".
Capture the resulting PR URL.
9. Record the outcome
Update runs/<run-id>/manifest.yml:
labs:
<slug>:
status: issue_and_pr_filed
issue_url: <from issue-filer>
issue_action: created | commented
pr_url: <captured above>
pr_action: created | appended | skipped_no_changes
finished_at: <iso-timestamp>
Append to runtime/audit-history.yml:
- run_id: <run-id>
lab_slug: <slug>
status: issue_and_pr_filed
issue_url: <...>
pr_url: <...>
findings_applied: <N>
findings_deferred: <N>
Write runs/<run-id>/labs/<slug>/pr-url.txt containing just the PR URL for the orchestrator's end-of-run summary.
Important rules
- Never force-push.
pr_append.allow_force_push is false and remains so.
- Dedup is OPEN-PR-scoped. Only an open fix-PR for the slug suppresses a new PR. Merged/closed PRs never block a new one — that's the whole point of the run-unique branch name. See [[feedback_fresh_branch_per_pr]].
- One open PR per lab. If an open fix-PR already exists, append to it; never open a second open PR for the same lab in the same breath.
- Never modify a closed/merged PR's branch. Always check open-PR state before pushing. See [[feedback_no_push_to_merged_pr]].
- Respect same-author guardrail. If the current
gh user does not match the PR author on an existing open PR, abort the append step and leave the issue-filer's record as the only output for this lab.
- Restore the user's working tree. If you stashed changes at the start,
git stash pop (or leave the stash with a clear label and tell the user how to recover) — never silently lose work.
- Defer scene-scope corrections. Anything bigger than a phrase- or step-scope replacement should land in the PR body's "Findings deferred to human review" section, not in the commit. Large rewrites (e.g., adding a whole new use case) should be handled by the orchestrator at audit time, not auto-applied here.
What to do when stuck
original_text not found in markdown: lab has drifted since the audit. Record in pr-apply-log.json and continue.
proposed_screenshot_replacement source missing: subagent didn't save the file. Skip the image update and record in pr-apply-log. The PR still ships the markdown corrections; the screenshot finding remains in the issue for human attention.
- Branch is up-to-date with main and no changes apply: still no-op — set
pr_action: skipped_no_changes and don't push an empty commit.
- An open PR exists but the author/mergeable guardrail fails: abort the PR step with the reason recorded; do NOT open a competing second PR. The issue-filer's record stands and the user is told to inspect the open PR.
gh pr create fails: leave the branch pushed (the commits are valuable), record the error in pr-apply-log.json, and tell the user the manual PR-create command they can run.