You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
doctor is the pipeline's fresh-repo preflight — "does this repo meet the prerequisites?" Its Tier-1 required-label list is a hand-written heredoc, and it is narrower than what triage consumes to reach a terminal outcome. It also verifies no milestone at all. A repo can therefore pass doctor green and still leave triage with no non-kill home for any issue.
The delta (re-verified by triage against origin/main)
claude-plugins/kampus-pipeline/skills/doctor/doctor.sh, check 1c — the LABELS heredoc — enumerates exactly 15 labels (status: spine ×6, type: ×6, p0/p1/p2) and reports all 15 required pipeline labels exist. Confirmed absent from that set, and confirmed by a grep: the file contains zero occurrences of milestone.
claude-plugins/kampus-pipeline/skills/triage/SKILL.md, Step 6 "Assign a home — milestone, standing lane, or kill (required)" gives triage exactly three terminal homes, and doctor verifies the prerequisites of neither non-kill one:
Triage outcome
What it needs in the repo
Doctor checks it?
Home in an arc/campaign milestone
an existing open milestone (triage may never POST one — ADR 0072 §3)
No — no milestones call anywhere in the file
Exempt with a standing lane
wayfinder:backlogoraxis:pipeline-hardening ("exactly two, no third")
No — neither label is in the required set
Kill (close not-planned)
nothing
n/a — and it is agent-filed-only, so it is not a general fallback
Same class, one surface over: claude-plugins/kampus-pipeline/skills/wayfinder/SKILL.md keys its map issue on a wayfinder:map label likewise absent from doctor's list — evidence the gap is a maintenance property of the hand-written list, not a single omission.
Not part of this delta:p3. The triage skill defines exactly p0/p1/p2 (grep for p3 in claude-plugins/kampus-pipeline/skills/triage/SKILL.md returns nothing). The separate p3 mismatch in epic-ledger is #4101 and stays there.
Triage note 1 — does it bite this repo? Ruled: real here, harm lands in a foreign repo
The two surfaces genuinely disagree in this repo today — that is a property of the files, not of repo state, and it is verifiable by reading them. But the harm doctor exists to prevent cannot occur here: this repo has both standing-lane labels and 14 open milestones, so triage homes fine. The inconsistency is masked, not absent — doctor would pass identically if they were deleted.
That is exactly the blast radius a preflight has by construction: its consumer is the fresh-repo adopter, not the repo it was written in. So this is a foreign-repo-facing correctness defect with a live root cause here, which is why it homes in the Pipeline Anywhere campaign rather than reading as speculative hardening.
Triage note 2 — derived vs. extended: ruled a narrow fix, not a recorded decision
The filer left this deliberately unruled. Triage's ruling: the fork does not need an ADR, so this is type:bug, not type:decision. Three reasons:
Both branches require the same immediate correction. The two standing-lane labels are missing from doctor's set today under either answer. There is no branch on which the narrow fix is wrong.
"Derive from what the skills consume" has no real mechanism at this surface.doctor.sh is a standalone bash script; deriving from skill prose means parsing markdown, which is a worse coupling than the heredoc. The plausible single source is a data constant, not a skill document.
That data constant already has an owner.Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272 (triaged, p1, same milestone) builds a shared vocabulary preflight in packages/pipeline-cli/ that asserts the required label universe exists in the resolved target repo. So "should there be one source?" is a single-sourcing follow-on with an in-flight owner, not an unmade governance decision. What this issue must avoid is minting a third independent vocabulary list.
Triage note 3 — tier the additions; do not red a legitimately-fresh repo
Precision the report blurs, and the coder needs it: with the two standing-lane labels present, triage can terminate (outcome 2) with no milestone at all. So the milestone check and the label additions are not the same severity:
The two standing-lane labels are Tier 1. Without them triage's only non-kill home is unreachable, and kill is agent-filed-only.
Milestone existence is a WARN, not a Tier-1 fail. A freshly-adopted repo legitimately has no milestones yet — creating them is a human roadmap act (ADR 0072 §3), so failing Tier 1 on their absence would red the correct state of a brand-new adopter. It belongs as an advisory that names the consequence ("triage can exempt but cannot home into an arc").
wayfinder:map tracks whatever tier the wayfinder skill sits in; it is an ideation surface, not first-run-blocking.
The literal 15 in all 15 required pipeline labels exist must become the derived count, or it drifts on the next edit.
ship-it's enqueue needs repo auto-merge + a queue ruleset no fresh repo has #4303 (repo allow_auto_merge + merge-queue ruleset, also unchecked by doctor) — separate unit. Same file, different proposition and a different API surface (repo settings / rulesets vs. labels + milestones). Editing the same script is not the shared-fix test; the fixes share no logic. Keep them independently pickable; a coder may land both in one PR if they happen to be picked together, but neither blocks the other.
§CP — this fix is human-merged
doctor.sh is a control-plane file. The §CP path clause covers claude-plugins/kampus-pipeline/skills/**/*.sh at any depth and names doctor/doctor.sh explicitly as an instance (claude-plugins/kampus-pipeline/skills/gh-issue-intake-formats.md §CP; ADR 0174). Note the asymmetry that makes this easy to get wrong: the doctorskill directory is deliberately out of §CP, but its .sh helper is in. So the PR is blocking / human-merged, and gets a review-skill verdict.
Acceptance criteria
doctor.sh check 1c includes wayfinder:backlog and axis:pipeline-hardening — the two labels that make triage's standing-lane exemption reachable — each with a create-command fix line like every other required label.
The wayfinder:map label is covered at the tier matching the wayfinder skill's role, or its exclusion is recorded in a comment so the omission is a decision rather than drift.
Doctor reports on open milestone existence as a non-blocking WARN (never a Tier-1 fail), naming the consequence: triage can exempt to a standing lane but cannot home an issue into an arc/campaign.
The success message no longer hardcodes 15 — the count is derived from the list.
Verified by running doctor.sh against a repo (or a stubbed label read) missing the two standing-lane labels: it exits non-zero and names them.
Original report (verbatim)
Summary
doctor is the pipeline's "is this repo ready?" preflight, and its required-label list is hand-maintained. It is narrower than what triage actually consumes: it verifies 15 labels but neither of the two standing-lane labels (wayfinder:backlog, axis:pipeline-hardening) that triage needs to reach a terminal outcome, and it verifies no milestone at all. So a repo can pass doctor green and still leave triage with no non-kill home for any issue.
What I was doing
Working the H4 residual from the fresh-repo halt list on #4268 (#4268 (comment)). H4 asserts a gap between doctor and triage; unlike most of that list, this one is checkable by reading both files, so I verified it directly against origin/main rather than leaving it as a prediction.
What I observed
Observed first-hand (both files read at origin/main).
claude-plugins/kampus-pipeline/skills/doctor/doctor.sh, check 1c (the LABELS heredoc, lines 49-82), enumerates exactly 15 required labels and prints all 15 required pipeline labels exist on success:
This is Tier 1, described in the file header as "load-bearing — gh auth + required labels. Without these the first report/triage run fails deep inside a gh api call."
claude-plugins/kampus-pipeline/skills/triage/SKILL.md, "Assign a home — milestone, standing lane, or kill (required)" (lines ~635-680), makes homing mandatory before status:triaged is stamped, with exactly three outcomes:
an existing open milestone (gh api "repos/$REPO/milestones?state=open"), where creating one is forbidden: "Existing open milestones only — triage NEVER creates one. … Do notPOST a new milestone, ever."
one of exactly two standing-lane labels — wayfinder:backlog or axis:pipeline-hardening — "exactly two, no third";
a kill (close not-planned, agent-filed issues only).
The delta, stated precisely:
wayfinder:backlog and axis:pipeline-hardening are not in doctor's required set. These are the only two labels that make outcome 2 reachable.
Doctor never checks that any open milestone exists, so outcome 1 is unverified too. Doctor reads repos/$REPO/labels; it makes no milestones call anywhere in the file.
Correction to H4's parenthetical: p3 is not part of the delta. Triage's priority table defines exactly p0/p1/p2 and there is no p3 in the triage skill or in packages/pipeline-cli/src. Doctor's omission of p3 is not a doctor↔triage gap. (A separate p3-related mismatch in epic-ledger is already tracked as epic-ledger floor rejects p3: PRIORITY_LABELS is p0-p2, so any p3 epic child fails the review-plan gate #4101 — different surface, not this.)
Same class, one surface over:wayfinder/SKILL.md keys its map issue on a wayfinder:map label that is likewise absent from doctor's list — evidence the gap is a maintenance property of the hand-written list, not a one-off omission.
Net: a repo where doctor passes green can still have triage unable to terminate on anything but a kill, because both non-kill homes depend on repo state doctor never verified.
Distinguished — predicted, not observed: the downstream foreign-repo consequence (an adopter stands the pipeline up, doctor reports ready, and every triage run is forced to kill-or-stall) follows from reading the two files but has not been run. The delta above is observed; that consequence is inferred.
Why it matters
This is a drift between two surfaces that each look authoritative, where neither owns the other. doctor exists precisely to answer "is this repo ready?"; a green doctor that still leaves triage unable to home an issue means doctor is answering a narrower question than the one its callers ask it. The failure lands as a confusing mid-run stall attributed to triage, not as an unmet prerequisite with a name — which is the same silent-failure signature the halt list flags throughout.
It is also not only a foreign-repo problem: the two surfaces disagree in this repo today. This repo happens to have the standing-lane labels and open milestones, so the gap is currently masked rather than absent — doctor would pass identically if they were deleted.
The structural question this raises, which I am flagging rather than answering: should doctor's required set be derived from what the skills actually consume, rather than maintained as a parallel hand-written list? A hand-written list drifts every time a skill's vocabulary moves, and the wayfinder:map instance suggests it already has.
Open questions, both deliberately unruled here:
Should the required set be derived from skill consumption, or stay hand-maintained?
Separately and independently: should the two standing-lane labels simply be added to doctor's list as a narrow fix?
I am not ruling between the narrow fix and the derived one, and not picking a mechanism for either.
Pointers
claude-plugins/kampus-pipeline/skills/doctor/doctor.sh — check 1c, the LABELS heredoc (lines 49-82) and the all 15 required pipeline labels exist message.
claude-plugins/kampus-pipeline/skills/triage/SKILL.md — "Assign a home — milestone, standing lane, or kill (required)" (~lines 635-680), and the priority table above it (~lines 590-600).
claude-plugins/kampus-pipeline/skills/wayfinder/SKILL.md — the wayfinder:map label, same class of omission.
ADR 0208 (standing-lane exemption), ADR 0072 (milestones are a human act), ADR 0092 (fail-closed guards).
Suggested next step (non-binding)
A guess only: read doctor's required set and triage's three homing outcomes side by side and decide the derived-vs-narrow question first, before touching either file — the answer determines whether this is a three-line heredoc edit or a new shared source of truth the skills and doctor both read. Whichever way it goes, the milestone-existence check (outcome 1) looks like it needs the same treatment as the two labels, not a separate pass.
Filed by an agent · session 51a8e31d-bfd4-4627-af80-f00fcd0805c5 · branch main · 2026-07-26T20:38:15Z
doctoris the pipeline's fresh-repo preflight — "does this repo meet the prerequisites?" Its Tier-1 required-label list is a hand-written heredoc, and it is narrower than whattriageconsumes to reach a terminal outcome. It also verifies no milestone at all. A repo can therefore passdoctorgreen and still leavetriagewith no non-kill home for any issue.The delta (re-verified by triage against
origin/main)claude-plugins/kampus-pipeline/skills/doctor/doctor.sh, check1c— theLABELSheredoc — enumerates exactly 15 labels (status:spine ×6,type:×6,p0/p1/p2) and reportsall 15 required pipeline labels exist. Confirmed absent from that set, and confirmed by agrep: the file contains zero occurrences ofmilestone.claude-plugins/kampus-pipeline/skills/triage/SKILL.md, Step 6 "Assign a home — milestone, standing lane, or kill (required)" gives triage exactly three terminal homes, and doctor verifies the prerequisites of neither non-kill one:POSTone — ADR 0072 §3)milestonescall anywhere in the filewayfinder:backlogoraxis:pipeline-hardening("exactly two, no third")Same class, one surface over:
claude-plugins/kampus-pipeline/skills/wayfinder/SKILL.mdkeys its map issue on awayfinder:maplabel likewise absent from doctor's list — evidence the gap is a maintenance property of the hand-written list, not a single omission.Not part of this delta:
p3. The triage skill defines exactlyp0/p1/p2(grepforp3inclaude-plugins/kampus-pipeline/skills/triage/SKILL.mdreturns nothing). The separatep3mismatch inepic-ledgeris #4101 and stays there.Triage note 1 — does it bite this repo? Ruled: real here, harm lands in a foreign repo
The two surfaces genuinely disagree in this repo today — that is a property of the files, not of repo state, and it is verifiable by reading them. But the harm doctor exists to prevent cannot occur here: this repo has both standing-lane labels and 14 open milestones, so triage homes fine. The inconsistency is masked, not absent — doctor would pass identically if they were deleted.
That is exactly the blast radius a preflight has by construction: its consumer is the fresh-repo adopter, not the repo it was written in. So this is a foreign-repo-facing correctness defect with a live root cause here, which is why it homes in the Pipeline Anywhere campaign rather than reading as speculative hardening.
Triage note 2 — derived vs. extended: ruled a narrow fix, not a recorded decision
The filer left this deliberately unruled. Triage's ruling: the fork does not need an ADR, so this is
type:bug, nottype:decision. Three reasons:doctor.shis a standalone bash script; deriving from skill prose means parsing markdown, which is a worse coupling than the heredoc. The plausible single source is a data constant, not a skill document.p1, same milestone) builds a shared vocabulary preflight inpackages/pipeline-cli/that asserts the required label universe exists in the resolved target repo. So "should there be one source?" is a single-sourcing follow-on with an in-flight owner, not an unmade governance decision. What this issue must avoid is minting a third independent vocabulary list.Triage note 3 — tier the additions; do not red a legitimately-fresh repo
Precision the report blurs, and the coder needs it: with the two standing-lane labels present, triage can terminate (outcome 2) with no milestone at all. So the milestone check and the label additions are not the same severity:
wayfinder:maptracks whatever tier thewayfinderskill sits in; it is an ideation surface, not first-run-blocking.The literal
15inall 15 required pipeline labels existmust become the derived count, or it drifts on the next edit.Triage note 4 — boundaries with #4272 and #4303
packages/pipeline-cli/src/tools/**closing the per-issue vacuous-pass hole inhoming-guard/pitch-guard. It names neitherdoctor.shnor the homing requirement. The shared-fix test fails: no single change resolves both, and this issue's milestone half is not a label at all. The correct relation is consumption: if Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272's vocabulary set has landed by the time this is picked up, doctor should read it rather than grow a parallel list. A coder is reported in flight on Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272 (triage did not verify a claim on it — itsassigneeslist read empty at triage time), so nothing here was changed on it.allow_auto_merge+ merge-queue ruleset, also unchecked by doctor) — separate unit. Same file, different proposition and a different API surface (repo settings / rulesets vs. labels + milestones). Editing the same script is not the shared-fix test; the fixes share no logic. Keep them independently pickable; a coder may land both in one PR if they happen to be picked together, but neither blocks the other.§CP — this fix is human-merged
doctor.shis a control-plane file. The §CP path clause coversclaude-plugins/kampus-pipeline/skills/**/*.shat any depth and namesdoctor/doctor.shexplicitly as an instance (claude-plugins/kampus-pipeline/skills/gh-issue-intake-formats.md§CP; ADR 0174). Note the asymmetry that makes this easy to get wrong: thedoctorskill directory is deliberately out of §CP, but its.shhelper is in. So the PR is blocking / human-merged, and gets areview-skillverdict.Acceptance criteria
doctor.shcheck1cincludeswayfinder:backlogandaxis:pipeline-hardening— the two labels that make triage's standing-lane exemption reachable — each with a create-command fix line like every other required label.wayfinder:maplabel is covered at the tier matching thewayfinderskill's role, or its exclusion is recorded in a comment so the omission is a decision rather than drift.15— the count is derived from the list.claude-plugins/kampus-pipeline/skills/triage/SKILL.mdStep 6 and Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272 naming the intended single source.doctor.shagainst a repo (or a stubbed label read) missing the two standing-lane labels: it exits non-zero and names them.Original report (verbatim)
Summary
doctoris the pipeline's "is this repo ready?" preflight, and its required-label list is hand-maintained. It is narrower than whattriageactually consumes: it verifies 15 labels but neither of the two standing-lane labels (wayfinder:backlog,axis:pipeline-hardening) that triage needs to reach a terminal outcome, and it verifies no milestone at all. So a repo can passdoctorgreen and still leave triage with no non-kill home for any issue.What I was doing
Working the H4 residual from the fresh-repo halt list on #4268 (#4268 (comment)). H4 asserts a gap between
doctorandtriage; unlike most of that list, this one is checkable by reading both files, so I verified it directly againstorigin/mainrather than leaving it as a prediction.What I observed
Observed first-hand (both files read at
origin/main).claude-plugins/kampus-pipeline/skills/doctor/doctor.sh, check1c(theLABELSheredoc, lines 49-82), enumerates exactly 15 required labels and printsall 15 required pipeline labels existon success:status:needs-triage,status:needs-info,status:planned,status:triaged,status:planning,status:awaiting-releasetype:bug,type:chore,type:decision,type:epic,type:feature,type:investigationp0,p1,p2This is Tier 1, described in the file header as "load-bearing — gh auth + required labels. Without these the first report/triage run fails deep inside a
gh apicall."claude-plugins/kampus-pipeline/skills/triage/SKILL.md, "Assign a home — milestone, standing lane, or kill (required)" (lines ~635-680), makes homing mandatory beforestatus:triagedis stamped, with exactly three outcomes:gh api "repos/$REPO/milestones?state=open"), where creating one is forbidden: "Existing open milestones only — triage NEVER creates one. … Do notPOSTa new milestone, ever."wayfinder:backlogoraxis:pipeline-hardening— "exactly two, no third";The delta, stated precisely:
wayfinder:backlogandaxis:pipeline-hardeningare not in doctor's required set. These are the only two labels that make outcome 2 reachable.repos/$REPO/labels; it makes nomilestonescall anywhere in the file.p3is not part of the delta. Triage's priority table defines exactlyp0/p1/p2and there is nop3in the triage skill or inpackages/pipeline-cli/src. Doctor's omission ofp3is not a doctor↔triage gap. (A separatep3-related mismatch inepic-ledgeris already tracked as epic-ledger floor rejects p3: PRIORITY_LABELS is p0-p2, so any p3 epic child fails the review-plan gate #4101 — different surface, not this.)wayfinder/SKILL.mdkeys its map issue on awayfinder:maplabel that is likewise absent from doctor's list — evidence the gap is a maintenance property of the hand-written list, not a one-off omission.Net: a repo where doctor passes green can still have triage unable to terminate on anything but a kill, because both non-kill homes depend on repo state doctor never verified.
Distinguished — predicted, not observed: the downstream foreign-repo consequence (an adopter stands the pipeline up, doctor reports ready, and every triage run is forced to kill-or-stall) follows from reading the two files but has not been run. The delta above is observed; that consequence is inferred.
Why it matters
This is a drift between two surfaces that each look authoritative, where neither owns the other.
doctorexists precisely to answer "is this repo ready?"; a green doctor that still leaves triage unable to home an issue means doctor is answering a narrower question than the one its callers ask it. The failure lands as a confusing mid-run stall attributed to triage, not as an unmet prerequisite with a name — which is the same silent-failure signature the halt list flags throughout.It is also not only a foreign-repo problem: the two surfaces disagree in this repo today. This repo happens to have the standing-lane labels and open milestones, so the gap is currently masked rather than absent — doctor would pass identically if they were deleted.
The structural question this raises, which I am flagging rather than answering: should doctor's required set be derived from what the skills actually consume, rather than maintained as a parallel hand-written list? A hand-written list drifts every time a skill's vocabulary moves, and the
wayfinder:mapinstance suggests it already has.Open questions, both deliberately unruled here:
I am not ruling between the narrow fix and the derived one, and not picking a mechanism for either.
Pointers
claude-plugins/kampus-pipeline/skills/doctor/doctor.sh— check1c, theLABELSheredoc (lines 49-82) and theall 15 required pipeline labels existmessage.claude-plugins/kampus-pipeline/skills/triage/SKILL.md— "Assign a home — milestone, standing lane, or kill (required)" (~lines 635-680), and the priority table above it (~lines 590-600).claude-plugins/kampus-pipeline/skills/wayfinder/SKILL.md— thewayfinder:maplabel, same class of omission.packages/pipeline-cli/src/tools/**and builds a vocabulary preflight there; it names neitherdoctor.shnor the milestone/standing-lane homing requirement, and its ruling is that the vocabulary is governance, not configuration. This issue is the doctor↔triage consistency gap — an existing preflight surface being narrower than its consumer — which is adjacent to Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272 and not covered by it. If a reviewer judges the two should merge, that is a triage call; a coder is in flight on Pipeline label vocabulary + roadmap table + §CP path are hardcoded with no config seam #4272, so I did not assume it.Suggested next step (non-binding)
A guess only: read doctor's required set and triage's three homing outcomes side by side and decide the derived-vs-narrow question first, before touching either file — the answer determines whether this is a three-line heredoc edit or a new shared source of truth the skills and doctor both read. Whichever way it goes, the milestone-existence check (outcome 1) looks like it needs the same treatment as the two labels, not a separate pass.
Filed by an agent · session
51a8e31d-bfd4-4627-af80-f00fcd0805c5· branchmain· 2026-07-26T20:38:15Z