Repository navigation
persona-mention router ignores stop_markers (needs-human-review, dev-lead:needs-human) — a human hold stops dev-lead but not a mentioned persona #1133
Description
Story
As a maintainer who applies needs-human-review to stop automation on an item,
I want a mentioned persona to honour that brake the way dev-lead does,
so that a human hold stops every persona surface, not only the event-driven ones.
Filed here from combsmith/.github-private#1745, because the mention path's gating lives in this repo's persona-mention-reusable.yml. .github-private reaches it through a thin caller stub pinned to persona-mention/v1-next, which AGENTS.md forbids patching. The consuming-side work (reconciling interaction.yml, the validator) stays on .github-private#1745 and must not land until this ships.
The gap (verified on main, 2026-09-13)
Each persona's contract in petry-projects/.github-private (personas/<id>/interaction.yml) declares stop markers. For example, qa-lead declares:
stop_markers:
- qa-lead:hands-off
- needs-human-review
- dev-lead:needs-humanThe router honours only the first. In .github/workflows/persona-mention-reusable.yml, the only label check in the per-slug loop is the opt-out:
if [ -n "$opt_out" ] && printf '%s\n' "$LABELS" | grep -qxF "$opt_out"; then
echo "Item carries '${opt_out}' — persona '${slug}' is opted out here. Skipping."needs-human-review and dev-lead:needs-human are never consulted, so a mention on a held item still dispatches the persona. The pull_request surface in .github-private (scripts/qa-lead-advisory-gate.sh) honours all three markers, which leaves the same persona inconsistent across its surfaces. This is fail-open in the direction that matters.
Decisions — pre-answered (implement them, do not re-ask)
- Put the check in the router (
persona-mention-reusable.yml), in the per-slug loop right after the opt-out check. The router already holds$LABELS/$LABELS_KNOWN; the runner does not. - Derive the markers from the persona's own contract; never write them as literals. Fetch
personas/<slug>/interaction.ymlfrompetry-projects/.github-privateat the same ref and by the same unauthenticatedraw.githubusercontentpattern as the manifest (pm_manifest_url). Addpm_interaction_urland apm_stop_markersquery toscripts/lib/persona-mention.shalongsidepm_mention_decision. The contract is then true by construction, and no marker list is duplicated across repos (.github-private#1745 AC Add multi-agent isolation strategy using git worktrees #2). - Fetch failures mirror the manifest fetch exactly. A non-200, non-404 response is
exit 1: never read a fetch failure as "no stop markers". A 404 for a persona whose manifest exists means refuse to route that slug with::warning::no interaction contract for '<slug>'. Before relying on that, list whichpersonas/*/in .github-private lackinteraction.ymland put the list in the PR. If any persona lacks one, say so plainly; do not silently fall back to routing on the opt-out alone. - Discussions:
LABELS_KNOWN=false, so stop markers cannot be evaluated, exactly as the opt-out cannot today. Keep the existing discussion semantics (read-mode routes; write-mode is already refused because the gate cannot be verified). Document that explicitly in the workflow's header comment and instandards/persona-standards.mdnext to §4 rule 4. Do not leave it implicit. - Do not repoint any caller stub and do not cut a channel tag. After merge, a maintainer promotes
persona-mention/v1-nextviacut-release.sh; note that step in the PR description.
Acceptance Criteria
- A mention on an issue or PR carrying any of the persona's declared
stop_markersis skipped with a log line naming the marker, and dispatches nothing. - The marker list is read from
interaction.yml; there is no literalneeds-human-reviewin the routing logic. - An
interaction.ymlfetch that is neither 200 nor 404 fails the job (fail-closed). A 404 refuses to route that slug. - Discussion behaviour is stated in the workflow header and in
persona-standards.md. - Tests in the existing persona-mention test suite (
persona-mention-tests.yml) cover: marker present → skipped; marker absent → routed; opt-out still honoured; fetch 5xx → failure; 404 → refused; discussion → documented behaviour. shellcheck --severity=warning -xis clean, and SHA pins are unchanged.
Budget guard
- Helper functions, the router check and tests (AC Addressing PR comments #1–Add coding standards & principles section to AGENTS.md #3, Add stacked PR strategy and Epic-level workflow guidance #5). This is the commit that matters.
- Documentation (AC Add workflow, environment, and orchestration guidance #4).
If you run low on budget, stop after step 1 with a green PR and list what remains.
References
- Persona mention surface ignores needs-human-review / dev-lead:needs-human — a human brake stops the PR gate but not a mention .github-private#1745 — origin; carries the consuming-side ACs Add workflow, environment, and orchestration guidance #4–feat: add Structured Logging and CQRS standards #6
- Epic: qa-lead — close the gap between the declared persona and the served one .github-private#1643 — qa-lead epic
.github/workflows/persona-mention-reusable.yml,scripts/lib/persona-mention.sh.github-privatescripts/lib/pr-automation-budget.sh(pr_has_escalation_label) — the canonical marker check on the event-driven surface
Activity
don-petry commentedon Sep 13, 2026
Dev-Lead Implementation Plan
Issue: #1133 — persona-mention router ignores stop_markers
Scope
Teach the persona-mention router to honour each persona's declared
stop_markers(read from its owninteraction.ymlcontract) so a human hold —needs-human-review,dev-lead:needs-human,<id>:hands-off— stops a mentioned persona exactly as it stops the event-driven surfaces, with no marker list duplicated as a literal.Implementation Checklist
scripts/lib/persona-mention.sh: addpm_interaction_url <slug>(mirrorspm_manifest_url, pathpersonas/<slug>/interaction.yml, same unauthenticated raw pattern).scripts/lib/persona-mention.sh: addpm_stop_markers <interaction-yaml>— the pure query that emits the contract's declaredstop_markers, one per line.scripts/lib/persona-mention.sh: addpm_first_stop_marker <interaction-yaml>— reads the item's labels from stdin, emits the first declared marker present (the pure "is this item held" decision; newline-delimited whole-line match, safe for label names with spaces).scripts/lib/persona-mention.sh: addpm_fetch_disposition <http_code>— pure classification (read|absent|fail) mirroring the manifest-fetch status handling, so the fetch decision is unit-testable..github/workflows/persona-mention-reusable.yml: in the per-slug loop, right after the opt-out check, fetchinteraction.yml(unauthenticated, mirroring the manifest fetch: 200 → read; 404 →::warning::no interaction contract for '<slug>'and refuse to route that slug; any other status →exit 1), then skip the slug (naming the marker) if any declared marker is present. Guarded byLABELS_KNOWNso discussions keep their existing semantics..github/workflows/persona-mention-reusable.yml: document the discussion behaviour in the header comment.standards/persona-standards.md: document the discussion behaviour next to §4 rule 4.Tests to Write (tests/persona_mention.bats, gated by persona-mention-tests.yml)
pm_interaction_urlbuilds the raw contract path; honoursPERSONA_REF.pm_stop_markersreads the declared markers; empty when none/absent (AC Add multi-agent isolation strategy using git worktrees #2).pm_first_stop_marker: marker present → emits it (→ skipped); marker absent → empty (→ routed); opt-out label present in the marker set is honoured.pm_fetch_disposition: 200 → read; 404 → absent (refused); 5xx/000 → fail (fail-closed) (AC Add coding standards & principles section to AGENTS.md #3).Test Command
bats tests/persona_mention.bats(plusbash .dev-lead/scripts/dev-lead-lint.shfor shellcheck)Interaction contracts audit
I will list which
personas/*/in.github-privatelack aninteraction.ymland include it in the PR (per decision 3). No caller stub is repointed; a maintainer promotespersona-mention/v1-nextviacut-release.shafter merge.don-petry commentedon Sep 13, 2026
Dev-Lead: Progress (in progress — not yet durable)
Plan Execution (intended)
pm_interaction_url,pm_stop_markers,pm_first_stop_marker, andpm_fetch_dispositiontoscripts/lib/persona-mention.sh(all pure/network-free).persona-mention-reusable.yml, in the per-slug loop right after the opt-out check, guarded byLABELS_KNOWN. The interaction-contract fetch mirrors the manifest fetch exactly (200 → read; 404 →::warning::no interaction contract+ refuse this slug; anything else →exit 1).standards/persona-standards.md§4 rule 4.tests/persona_mention.bats.Interaction-contract audit (decision 3 —
personas/*/in.github-private@main)Every persona declares an
interaction.ymlwithstop_markers— none lack one, so the 404-refuse path is a safety net that does not fire for any current persona:Local check output (provisional — not the pushed run)
Notes
persona-mention/v1-nextviacut-release.sh; the consuming-side work stays on.github-private#1745and must not land until this ships.pm_stop_markersreads.interaction.stop_markers(the real nested shape) with a.stop_markerstop-level fallback.200with an unparseable body propagates a failure rather than being read as "no markers", same direction as a 5xx.don-petry commentedon Sep 13, 2026
Dev-Lead: Implementation Complete — PR #1134
The implementation for issue #1133 is durable: commits are pushed and a pull request is open. This record is backed by verifiable artifacts you can confirm in one click.
0c579fb670b6c4a4d59049099baa3674206f276dReview happens on the PR. Acceptance criteria and test results are evidenced by the PR's diff and CI checks — not asserted here. If a later stage fails, this claim is retracted in place.
feat: implement issue #1133 — persona-mention router ignores stop_mar…