Skip to content

[Phase 3] Collapse the first repo's Class-1 event-driven stubs into agent-ingress.yml and rename the required checks #1729

Description

@github-actions

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

  1. 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).
  2. 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.
  3. 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.
  4. 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.
  5. 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

Dev Notes

  • 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.

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/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.

  1. 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)

  1. 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.
  2. 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.
  3. 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."
  4. 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)

  1. 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/.github standards/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

  1. 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.

Implement in this order; commit as you go

  1. AC6 — the collapse package (the deliverable that lives in this repo). The exact
    agent-ingress.yml content for markets built to the [Phase 1] Author the agent-ingress.yml canonical structure and freeze the if:-as-event-filter boundary #1724 structure; the enumerated eligible
    set with carve-outs justified; the old→new required-check name mapping; AC5 citing the
    baseline path. This must be complete on its own.
  2. 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.
  3. AC11 — STUB_REGISTRY entries for each collapsed role, and publish the canonical template
    under petry-projects/.github standards/workflows/.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    dev-leadFor dev-lead agent pickupinitiativeEpic / initiative tracking issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions