| name | mcs-lab-new-lab-pr |
| description | Open a PR on the active instance's lab repo (microsoft/mcs-labs by default) that adds a NEW lab built by mcs-lab-builder. Stages the assembled `labs/<slug>/README.md` + screenshots, applies the registration entry (root `lab-config.yml` + generator, or direct `_data/lab-config.yml` + `_labs/<slug>.md` writes — per the detected mechanism), commits everything in one commit on a run-unique branch off `origin/main`, and opens the PR. Invoked by mcs-lab-builder at B7 — NOT directly by the user. Do not use this for audit fixes (that is mcs-lab-fix-pr-filer, which patches an existing lab).
|
| allowed-tools | ["Read","Glob","Grep","Write","Edit","PowerShell","Bash(git*)","Bash(gh pr create:*)","Bash(gh pr list:*)","Bash(gh pr comment:*)"] |
mcs-lab-new-lab-pr (sub-skill)
Open the PR for a freshly-built lab. You are called by mcs-lab-builder at B7 with: build_id, mcs_labs_repo (resolved absolute path), slug, registration_mode (generate | direct), the lab metadata + order + journeys, the optional events list, the proposal_issue.number (the In-Progress type: new-lab issue opened at B3.5, if any), and the build workspace path runtime/builds/<build-id>/. The audit gate has already passed (or the user accepted a draft PR with residual findings listed by the orchestrator).
Why a separate skill from mcs-lab-fix-pr-filer. That filer's contract is: patch an existing _labs/<slug>.md from suggested_correction diffs and replace images, with OPEN-PR-dedup keyed to a lab's audit history. A new lab has no existing markdown to patch and no findings diffs — it adds a whole folder, a registration entry, and (in generate mode) generated output. Keeping this separate preserves both skills' contracts.
Flow
-
Prepare the working tree.
cd <mcs_labs_repo>
git fetch origin main
Stash any unrelated in-flight changes with a labeled stash and restore them at the end — never clobber the user's uncommitted work. Record whether a stash was created.
-
Create the branch (run-unique, off fresh main).
branch = build.issues.new_lab_pr.pr_branch_pattern # default "{branch_prefix}/new-lab-{slug}-{build_id}"
git checkout -b "<branch>" origin/main
The {build_id} suffix makes the branch unique — re-running build for the same slug yields a fresh build_id and a fresh branch, so a merged/closed prior PR never collides. (No open-PR append path: a new lab is one-shot.)
-
Stage the lab content.
- Copy
runtime/builds/<build_id>/draft/README.md → <mcs_labs_repo>/labs/<slug>/README.md.
- Copy
runtime/builds/<build_id>/draft/images/* → <mcs_labs_repo>/labs/<slug>/images/ (create the dir; same-name copy — the README references images/<file>).
- These may already be staged by the B6 gate; copying is idempotent.
-
Apply registration (per skills/mcs-lab-builder/references/lab-registration-spec.md):
- generate mode: add the one entry to the root
lab-config.yml (title, difficulty, duration, section, order, journeys, and events: [...] only if events is non-empty), then run the generator (pwsh -NoProfile -File <mcs_labs_repo>/scripts/Generate-Labs.ps1 -SkipPDFs or the path from config). Commit the generated _labs/<slug>.md + _data/lab-config.yml too.
- direct mode (current reality): add the lab entry directly to
_data/lab-config.yml (matching the existing lab_metadata / lab_journeys / lab_orders shape), and write _labs/<slug>.md directly — Jekyll frontmatter (layout: lab, title, order, , , , , , plus any /event keys only if attached) followed by the README body. If is non-empty, add the slug to each event's in .
Rules
- Branch off fresh
origin/main, never the user's current branch (the mcs-labs clone is often on a feature branch).
- One commit, no AI attribution (no
Co-Authored-By: Claude, no "Generated with…") — user preference.
- Never force-push. Never touch other labs' files.
- Restore stashed work even on early abort.
- Generated output is committed in generate mode so the PR builds the site without a maintainer re-running generation.
- If you close or supersede a PR you opened (without merging it), close its linked proposal issue in the same step.
Closes #<n> fires on merge only, so a non-merge close would otherwise leave the type: new-lab / status: in-progress proposal orphaned. Run gh issue close <proposal_issue.number> --repo {repo} --comment "Linked PR #<pr> was closed without merging." (skip when build.proposal_issue.close_orphaned_on_pr_close: false). Never depend on Closes to clean up a closed-unmerged PR.