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
As a fleet workflow maintainer,
I want collapse the eligible Class-1 event-driven thin-caller stubs in the first target repo into a single agent-ingress.yml and rename the branch-protection required checks to the new per-job names in the same change,
so that the first repo demonstrates the ADR-0007 collapse end-to-end with zero dropped triggers and no protection gap, validating the whole toolchain.
Acceptance Criteria
The eligible Class-1 event-driven thin-caller stubs (at minimum dev-lead.yml and pr-review-mention.yml; enumerate the FULL eligible set, EXCLUDING the pull_request_target carve-outs -- add-to-project.yml, dependabot-automerge.yml, dependabot-rebase.yml -- and Class-2/3 timers) are removed and replaced by the ingress form built to the Story-1 structure (one agent-ingress.yml, or a small permission-profile-grouped set, per the OQ1 file-count decision).
Every pre-collapse on: subscription is preserved in the union on: block; each role routes to its correct pinned reusable; validate-caller-inputs and validate-interaction-model are GREEN.
The old per-role required status-check names are renamed to the new per-job check names, coordinated ATOMICALLY with the collapse (the branch-protection ruleset rename is the admin step in the epic's untracked prerequisites), so protection never blocks merges on a check that no longer exists.
caller_stub_freeze baselines are regenerated for the collapsed jobs (Story 2 --update path), and fleet drift/remediate (Story 3) treats the collapsed repo correctly and does not attempt to resurrect a deleted stub.
Post-collapse the billed-run count stays within the epic's bound (union-subscribed non-matching jobs are if:-skipped), verified against the captured 1-week baseline; a trigger change to one collapsed role is now a one-file edit.
Tasks / Subtasks
Enumerate the eligible collapse set for the target repo, separating the pull_request_target carve-outs and Class-2/3 timers that stay as their own stubs. (AC: test issue from agent #1)
In .github-private the clearest eligible thin callers are dev-lead.yml (Class-1: pull_request, pull_request_review, pull_request_review_comment, issue_comment, issues:labeled, check_run, repository_dispatch) and pr-review-mention.yml (issue_comment, pull_request_review_comment, pull_request:review_requested). pr-review-trigger.yml is also ring-0 frozen (Story 2). Determine the full eligible set at implementation time -- several Class-1 workflows here (persona-mention, persona-runner, spec-drift, release-notes, dependency-advisory) carry inline org-private logic and are NOT thin callers, so they do NOT collapse.
Carve-outs are grounded and MUST stay separate: add-to-project.yml, dependabot-automerge.yml, dependabot-rebase.yml use pull_request_target; the ADR keeps pull_request_target roles on their own stub (a receiver/collapsed permission profile would inherit fork-PR secret access Actions withholds). auto-rebase.yml (push+workflow_dispatch) and the Class-2/3 timers keep their own files.
The atomicity requirement is real: the collapse renames the status-check names (they become per-job), and branch protection is repository-ruleset config that GITHUB_TOKEN cannot edit. Land the file collapse and the ruleset rename together (maintainer performs the ruleset half) or protection blocks merges on a vanished check. This is the untracked prerequisite on the epic.
Before merging, confirm Story 3's fleet_stub_remediate retarget is in place so a scheduled fleet run cannot 'remediate' the collapsed repo back to per-role stubs (the resurrection risk).
The behavior of every collapsed role is unchanged -- all logic stays in the pinned reusables; this story only moves the caller tier. Prove parity by keeping every prior on: subscription and every prior pin present in the ingress.
Project Structure Notes
Deletes the eligible per-role caller stubs and adds one agent-ingress.yml in the target repo; regenerates caller-stub-freeze fixtures; requires an out-of-band admin ruleset rename. If the first target is a leaf consumer rather than .github-private, the file work is a cross-repo PR that dev-lead cannot author from .github-private -- see the open question on target selection.
Story prepared by the BMAD Scrum Master (Bob) for epic #1723. Status: ready-for-dev.
Decisions folded in 2026-09-08 (these are ACs, not commentary)
First collapse target: petry-projects/markets
The epic's open question "which repo is the FIRST collapse target?" is decided: a leaf consumer, specifically petry-projects/markets. Reasoning, recorded so it can be argued with:
All six leaf consumers carry the same nine agentic stubs. After the pull_request_target carve-outs (add-to-project, dependabot-automerge, dependabot-rebase) roughly 5–6 stubs genuinely collapse — a real test of the file-count metric and of blast radius. .github-private was rejected as the pilot precisely because only ~2–3 of its Class-1 workflows are true thin callers; it would have produced a green pilot that proved almost nothing.
markets had 0 open PRs at decision time (clean cutover, nothing in flight to break) and the highest throughput of the leaves at ~24 PRs/30d — which matters because AC7 below requires an observed run per collapsed role, and a quiet repo could take weeks to produce that evidence.
ContentTwin (6 PRs/30d, 1 open) is the lower-blast-radius alternative if the pilot is judged too risky in an active repo; changing the target changes only this story.
Cross-repo execution — read before starting
dev-lead running in .github-private cannot author this collapse: the files live in petry-projects/markets. Do not attempt a cross-repo PR. This story's deliverable in .github-private is the prepared, reviewable collapse package, plus a companion issue filed in petry-projects/markets carrying it for that repo's own dev-lead to implement.
The collapse package is prepared and reviewable here: the exact agent-ingress.yml content for markets built to the Story [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 structure; the enumerated eligible stub set with the carve-outs excluded and justified; the old-to-new required-status-check name mapping; and the captured run-count baseline (below). A companion issue is filed in petry-projects/markets containing that package and labelled so that repo's dev-lead picks it up. This story closes when the companion issue is filed and the package is complete — the markets merge is tracked there, not here.
QA Lead test-risk inputs (folded in 2026-09-08)
Static green does not prove a role still fires. AC2's "validate-caller-inputs and validate-interaction-model are GREEN" proves the file parses and every pin resolves — it does not prove any role still triggers, which is exactly what the epic's success metric demands. One observed post-collapse run per collapsed role must be linked on the companion issue before it may close. Without this a dropped trigger is indistinguishable from success and the success metric is unverifiable.
The required-check rename needs a pre-merge gate and a tested rollback. AC3's "coordinated ATOMICALLY" is an intention, not a check. Required: (a) capture the current required-check name set BEFORE the PR; (b) assert the post-collapse job names produce exactly that set or a reviewed explicit mapping; (c) document and verify recovery for BOTH half-landed states — rename-without-collapse and collapse-without-rename — because the ruleset edit is a human admin step and the merge is not, so they can land out of order. This is the QA Lead's single escalated item: as originally written this story couples an irreversible protection change to a code change with no tested rollback path.
The 1-week billed-run baseline is a hard input, not a nice-to-have. AC5 depends on it and the epic lists it as an untracked prerequisite, but nothing blocks on its existence. The baseline must be captured and committed as a referenced artifact path before this story starts; AC5 must cite that path. Otherwise AC5 gets closed with "looks fine."
Skipped-job billing is measured, not assumed — and there are TWO skip kinds, which bill differently. ADR-0007 was corrected during review on exactly this point, so the AC follows it: (a) a job skipped by the job-level if: event filter is evaluated on the Actions service and never assigned a runner — no minutes; (b) a job whose changed-path check runs inside the reusable has already started and holds a runner for the diff step before skipping the role work — real, if small, minutes. Any collapsed role that relied on an event-level paths: filter (in this repo, the dependency-advisory shape) falls into (b) and pays a per-event runner cost that event-level paths: avoided entirely. Measure both kinds separately against the captured baseline; a single blended "skipped jobs are free" number does not satisfy this AC.
Added 2026-09-13 — deferred from PR #1771 review (#1726)
Register the collapsed roles in production drift monitoring.[Phase 2] Retarget fleet drift/remediate tooling to per-job extraction (never resurrect a collapsed stub) #1726 added the role selector to STUB_REGISTRY but, correctly for its scope, registered no collapsed role — the production registry still enrols only initiative-planner and initiative-driver, so the per-job extraction path is exercised by no production entry. When markets collapses: (a) publish the canonical agent-ingress.yml template under petry-projects/.githubstandards/workflows/ so each entry's canonical_path resolves rather than being tolerated as a 404; (b) add a STUB_REGISTRY entry per collapsed role (dev-lead, pr-review-mention, and every other collapsed job) with its role selector and its legacy_path for not-yet-collapsed repos; (c) show a fleet-monitor run in which each collapsed markets role classifies ALIGNED by job block — not MISSING — linked on the companion issue. Without this the drift tooling built in [Phase 2] Retarget fleet drift/remediate tooling to per-job extraction (never resurrect a collapsed stub) #1726 is correct but inert.
Added 2026-09-13 — template-drift expects a live ingress before rollout
Stop the template drift check from pointing anyone at a premature live ingress.scripts/template_stub_drift.sh (line 64, added by [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 / PR feat: implement issue #1724 — [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1764) already enrolls .github/workflows/agent-ingress.yml as an expected repo-template stub, so every template-drift run warns MISSING with the remedy "re-seed via scripts/seed-repo-template.sh". But seed-repo-template.sh deliberately never seeds it (REFERENCE_MANIFEST, reachable only via --emit-workflow), so that remedy can never clear the warning. The only thing that silences it is copying the --emit-workflow agent-ingress.yml output into repo-template/.github/workflows/ by hand. That content is a live workflow (real on: triggers, a dev-lead job pinned to dev-lead/v139-stable, secrets: inherit, contents: write), so doing it would ship an executing ingress to every new repo alongside the legacy dev-lead.yml, ahead of this pilot. Required: (a) until this story publishes the canonical ingress, either de-enroll the agent-ingress.yml row from the drift check or give reference rows their own non-actionable message ("expected; do not seed until [Phase 3] Collapse the first repo's Class-1 event-driven stubs into agent-ingress.yml and rename the required checks #1729"); (b) when markets collapses, seed the template in the same change that retires the per-role stubs it replaces, never alongside them; (c) a bats case showing a reference row cannot print the generic re-seed remedy.
Delivery brief — 2026-09-23 (read this section first; implement it, do not re-ask)
The AC9 baseline prerequisite is DISCHARGED. It is captured at docs/initiatives/agent-ingress-collapse-baseline.md (PR #1906). AC5 must cite that path. Do not
attempt to re-capture it — and do not treat "capture the baseline" as work remaining in this story.
Measured there, for petry-projects/markets, window 2026-09-15..2026-09-22:
Role
Stub
Pin
Runs
dev-lead
dev-lead.yml
dev-lead/v139-stable
584
pr-review-mention
pr-review-mention.yml
pr-review-mention/v2-stable
330
pr-auto-review
pr-auto-review.yml
pr-auto-review/v1-stable
208
pr-review
pr-review.yml
pr-review/stable ⚠ stale since 2026-06-20
187
ci-failure-analyst
ci-failure-analyst.yml
bare SHA e37e9eb4 ⚠ not a channel
33
collapsible total
1342
AC10 is already resolved for this repo and needs no new measurement design: no collapsible
caller in markets uses paths:/paths-ignore:, so every post-collapse skip is kind (a) — never
assigned a runner. State that, cite the baseline, and measure kind (a) only. The kind (b) case is .github-private's dependency-advisory shape and is out of scope here.
AC12 — the template-drift row. De-enroll agent-ingress.yml from scripts/template_stub_drift.sh, or give reference rows a non-actionable message, plus the bats
case. Small, self-contained, and it stops every template-drift run pointing at a remedy that
cannot clear.
AC11 — STUB_REGISTRY entries for each collapsed role, and publish the canonical template
under petry-projects/.githubstandards/workflows/.
AC6 (second half) — file the companion issue in petry-projects/markets carrying the
package, labelled so that repo's dev-lead picks it up. This story closes when the companion
issue is filed and the package is complete.
If budget runs short, stop after any completed step, put what is done in the PR description,
and say plainly which ACs remain. A partial package with an honest gap list is a good outcome.
Hard constraints
Do not author the collapse in markets from here. dev-lead running in .github-private
cannot author a cross-repo PR. The deliverable here is the package plus the companion issue.
Attempting the cross-repo PR is the single most likely way to waste this run.
Do not carry the two defective pins forward unexamined (AC6's mapping must name them): pr-review.yml → pr-review/stable has not moved since 2026-06-20 while pr-review/v1.10.0
exists; ci-failure-analyst.yml pins a bare SHA and receives no promotions.
Decide agent-shield.yml (16 runs) and dependency-audit.yml (16 runs) explicitly. Both are
first-party thin callers on Class-1 events, so the ADR's rule admits them, but both are gates
rather than agentic roles. AC1 requires the full eligible set enumerated — record include/exclude
and the reason. Do not decide either by omission.
AC3/AC8 (the escalated item) is a package deliverable, not a merge here. The branch-protection
rename is a human admin step in markets. Produce the name mapping, the pre-merge gate, and the
documented recovery for BOTH half-landed states (rename-without-collapse, collapse-without-rename).
Do not couple an irreversible protection change to a code merge without that written rollback.
AC7 (one observed run per collapsed role) is evidence gathered on the companion issue after the
markets merge — not something this story can satisfy. Say so rather than claiming it.
Steering reaches this agent through the issue title and body only — a question posted as an issue
comment will not be read and will not be answered. If something is still ambiguous, pick the reading
that best satisfies the ACs above, implement it, and state the assumption in the PR description.
Story
As a fleet workflow maintainer,
I want collapse the eligible Class-1 event-driven thin-caller stubs in the first target repo into a single agent-ingress.yml and rename the branch-protection required checks to the new per-job names in the same change,
so that the first repo demonstrates the ADR-0007 collapse end-to-end with zero dropped triggers and no protection gap, validating the whole toolchain.
Acceptance Criteria
agent-ingress.yml, or a small permission-profile-grouped set, per the OQ1 file-count decision).on:subscription is preserved in the unionon:block; each role routes to its correct pinned reusable; validate-caller-inputs and validate-interaction-model are GREEN.--updatepath), and fleet drift/remediate (Story 3) treats the collapsed repo correctly and does not attempt to resurrect a deleted stub.if:-skipped), verified against the captured 1-week baseline; a trigger change to one collapsed role is now a one-file edit.Tasks / Subtasks
Dev Notes
pull_request_target; the ADR keeps pull_request_target roles on their own stub (a receiver/collapsed permission profile would inherit fork-PR secret access Actions withholds). auto-rebase.yml (push+workflow_dispatch) and the Class-2/3 timers keep their own files.on:subscription and every prior pin present in the ingress.Project Structure Notes
Deletes the eligible per-role caller stubs and adds one agent-ingress.yml in the target repo; regenerates caller-stub-freeze fixtures; requires an out-of-band admin ruleset rename. If the first target is a leaf consumer rather than .github-private, the file work is a cross-repo PR that dev-lead cannot author from .github-private -- see the open question on target selection.
References
Likely target surface
.github/workflows/agent-ingress.yml (new).github/workflows/dev-lead.yml (delete).github/workflows/pr-review-mention.yml (delete)tests/fixtures/caller-stub-freeze/branch-protection ruleset (admin, out-of-band)Story prepared by the BMAD Scrum Master (Bob) for epic #1723. Status: ready-for-dev.
Decisions folded in 2026-09-08 (these are ACs, not commentary)
First collapse target:
petry-projects/marketsThe epic's open question "which repo is the FIRST collapse target?" is decided: a leaf consumer, specifically
petry-projects/markets. Reasoning, recorded so it can be argued with:pull_request_targetcarve-outs (add-to-project,dependabot-automerge,dependabot-rebase) roughly 5–6 stubs genuinely collapse — a real test of the file-count metric and of blast radius..github-privatewas rejected as the pilot precisely because only ~2–3 of its Class-1 workflows are true thin callers; it would have produced a green pilot that proved almost nothing.marketshad 0 open PRs at decision time (clean cutover, nothing in flight to break) and the highest throughput of the leaves at ~24 PRs/30d — which matters because AC7 below requires an observed run per collapsed role, and a quiet repo could take weeks to produce that evidence.ContentTwin(6 PRs/30d, 1 open) is the lower-blast-radius alternative if the pilot is judged too risky in an active repo; changing the target changes only this story.Cross-repo execution — read before starting
dev-lead running in
.github-privatecannot author this collapse: the files live inpetry-projects/markets. Do not attempt a cross-repo PR. This story's deliverable in.github-privateis the prepared, reviewable collapse package, plus a companion issue filed inpetry-projects/marketscarrying it for that repo's own dev-lead to implement.agent-ingress.ymlcontent formarketsbuilt to the Story [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 structure; the enumerated eligible stub set with the carve-outs excluded and justified; the old-to-new required-status-check name mapping; and the captured run-count baseline (below). A companion issue is filed inpetry-projects/marketscontaining that package and labelled so that repo's dev-lead picks it up. This story closes when the companion issue is filed and the package is complete — themarketsmerge is tracked there, not here.QA Lead test-risk inputs (folded in 2026-09-08)
validate-caller-inputsandvalidate-interaction-modelare GREEN" proves the file parses and every pin resolves — it does not prove any role still triggers, which is exactly what the epic's success metric demands. One observed post-collapse run per collapsed role must be linked on the companion issue before it may close. Without this a dropped trigger is indistinguishable from success and the success metric is unverifiable.if:event filter is evaluated on the Actions service and never assigned a runner — no minutes; (b) a job whose changed-path check runs inside the reusable has already started and holds a runner for the diff step before skipping the role work — real, if small, minutes. Any collapsed role that relied on an event-levelpaths:filter (in this repo, thedependency-advisoryshape) falls into (b) and pays a per-event runner cost that event-levelpaths:avoided entirely. Measure both kinds separately against the captured baseline; a single blended "skipped jobs are free" number does not satisfy this AC.Added 2026-09-13 — deferred from PR #1771 review (#1726)
roleselector toSTUB_REGISTRYbut, correctly for its scope, registered no collapsed role — the production registry still enrols onlyinitiative-plannerandinitiative-driver, so the per-job extraction path is exercised by no production entry. Whenmarketscollapses: (a) publish the canonicalagent-ingress.ymltemplate underpetry-projects/.githubstandards/workflows/so each entry'scanonical_pathresolves rather than being tolerated as a 404; (b) add aSTUB_REGISTRYentry per collapsed role (dev-lead,pr-review-mention, and every other collapsed job) with itsroleselector and itslegacy_pathfor not-yet-collapsed repos; (c) show a fleet-monitor run in which each collapsedmarketsrole classifiesALIGNEDby job block — notMISSING— linked on the companion issue. Without this the drift tooling built in [Phase 2] Retarget fleet drift/remediate tooling to per-job extraction (never resurrect a collapsed stub) #1726 is correct but inert.Added 2026-09-13 — template-drift expects a live ingress before rollout
scripts/template_stub_drift.sh(line 64, added by [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 / PR feat: implement issue #1724 — [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1764) already enrolls.github/workflows/agent-ingress.ymlas an expectedrepo-templatestub, so everytemplate-driftrun warns MISSING with the remedy "re-seed via scripts/seed-repo-template.sh". Butseed-repo-template.shdeliberately never seeds it (REFERENCE_MANIFEST, reachable only via--emit-workflow), so that remedy can never clear the warning. The only thing that silences it is copying the--emit-workflow agent-ingress.ymloutput intorepo-template/.github/workflows/by hand. That content is a live workflow (realon:triggers, adev-leadjob pinned todev-lead/v139-stable,secrets: inherit,contents: write), so doing it would ship an executing ingress to every new repo alongside the legacydev-lead.yml, ahead of this pilot. Required: (a) until this story publishes the canonical ingress, either de-enroll theagent-ingress.ymlrow from the drift check or give reference rows their own non-actionable message ("expected; do not seed until [Phase 3] Collapse the first repo's Class-1 event-driven stubs into agent-ingress.yml and rename the required checks #1729"); (b) whenmarketscollapses, seed the template in the same change that retires the per-role stubs it replaces, never alongside them; (c) a bats case showing a reference row cannot print the generic re-seed remedy.Delivery brief — 2026-09-23 (read this section first; implement it, do not re-ask)
The AC9 baseline prerequisite is DISCHARGED. It is captured at
docs/initiatives/agent-ingress-collapse-baseline.md(PR #1906). AC5 must cite that path. Do notattempt to re-capture it — and do not treat "capture the baseline" as work remaining in this story.
Measured there, for
petry-projects/markets, window2026-09-15..2026-09-22:dev-leaddev-lead.ymldev-lead/v139-stablepr-review-mentionpr-review-mention.ymlpr-review-mention/v2-stablepr-auto-reviewpr-auto-review.ymlpr-auto-review/v1-stablepr-reviewpr-review.ymlpr-review/stable⚠ stale since 2026-06-20ci-failure-analystci-failure-analyst.ymle37e9eb4⚠ not a channelAC10 is already resolved for this repo and needs no new measurement design: no collapsible
caller in
marketsusespaths:/paths-ignore:, so every post-collapse skip is kind (a) — neverassigned a runner. State that, cite the baseline, and measure kind (a) only. The kind (b) case is
.github-private'sdependency-advisoryshape and is out of scope here.Implement in this order; commit as you go
agent-ingress.ymlcontent formarketsbuilt to the [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 structure; the enumerated eligibleset with carve-outs justified; the old→new required-check name mapping; AC5 citing the
baseline path. This must be complete on its own.
agent-ingress.ymlfromscripts/template_stub_drift.sh, or give reference rows a non-actionable message, plus the batscase. Small, self-contained, and it stops every
template-driftrun pointing at a remedy thatcannot clear.
STUB_REGISTRYentries for each collapsed role, and publish the canonical templateunder
petry-projects/.githubstandards/workflows/.petry-projects/marketscarrying thepackage, labelled so that repo's dev-lead picks it up. This story closes when the companion
issue is filed and the package is complete.
If budget runs short, stop after any completed step, put what is done in the PR description,
and say plainly which ACs remain. A partial package with an honest gap list is a good outcome.
Hard constraints
marketsfrom here. dev-lead running in.github-privatecannot author a cross-repo PR. The deliverable here is the package plus the companion issue.
Attempting the cross-repo PR is the single most likely way to waste this run.
pr-review.yml→pr-review/stablehas not moved since 2026-06-20 whilepr-review/v1.10.0exists;
ci-failure-analyst.ymlpins a bare SHA and receives no promotions.agent-shield.yml(16 runs) anddependency-audit.yml(16 runs) explicitly. Both arefirst-party thin callers on Class-1 events, so the ADR's rule admits them, but both are gates
rather than agentic roles. AC1 requires the full eligible set enumerated — record include/exclude
and the reason. Do not decide either by omission.
rename is a human admin step in
markets. Produce the name mapping, the pre-merge gate, and thedocumented recovery for BOTH half-landed states (rename-without-collapse, collapse-without-rename).
Do not couple an irreversible protection change to a code merge without that written rollback.
markets merge — not something this story can satisfy. Say so rather than claiming it.
Steering reaches this agent through the issue title and body only — a question posted as an issue
comment will not be read and will not be answered. If something is still ambiguous, pick the reading
that best satisfies the ACs above, implement it, and state the assumption in the PR description.