diff --git a/.github/docs/state-machine.md b/.github/docs/state-machine.md index 397ab209583..47ff1843bf9 100644 --- a/.github/docs/state-machine.md +++ b/.github/docs/state-machine.md @@ -1,584 +1,375 @@ # dotnet/fsharp — Agentic State Machine -> **15 workflows documented.** Source: `.github/workflows/` · FULL_REWRITE (generator `d4fe5640de7eb85c`). +> **16 workflows documented.** Source: `.github/workflows/` · FULL_REWRITE (generator `f107bba1a1cd61dc`). -> **What this doc is.** A map of the 15 GitHub Actions workflows and AI agents in this repository — their triggers, control flow, side effects, and how they hand work to each other. Use it to understand "what runs when, who acts on what, and where does this label come from." +This document maps the 16 GitHub Actions workflows and AI agents in this repository — their triggers, control flow, side effects, and cross-workflow interactions. Audience: new engineers onboarding, PR reviewers, and automation auditors. Read top-down for context, or jump to the Handover Map for cross-workflow signals. ## Glossary -- **gh-aw** — `gh aw`, a GitHub CLI extension that compiles agentic workflow `.md` files (frontmatter + prompt) into runnable `.lock.yml` GitHub Actions workflows. See [github/gh-aw](https://github.com/githubnext/gh-aw). -- **gh-aw workflow (`*.md`)** — A workflow defined as a Markdown file with YAML frontmatter declaring triggers (`on:`), tools (`tools:`), and `safe-outputs:`. The body is the LLM prompt. -- **safe-outputs** — gh-aw's runtime permission and rate-limit framework. Each safe-output key (`create-pull-request`, `add-comment`, `add-labels`, `push-to-pull-request-branch`, `dispatch-workflow`, `noop`, etc.) constrains what side effects an agent run can produce: maximum count per run (`max:`), allowed file globs (`allowed-files:`), required label set, fallback behavior on validation failure. -- **noop** — A safe-output that lets an agent end a run cleanly with no side effects (no PR, no comment). `report-as-issue: false` suppresses opening an issue when the noop is the only output — i.e., a successful no-op is silent. -- **CCA (Copilot Coding Agent)** — Microsoft's hosted coding agent invoked via `create-agent-session`. The calling workflow hands off a task; CCA executes it asynchronously and writes results back to the repo. -- **state-store branch** — A dedicated git branch (e.g., `memory/repo-assist`, `safety/scanned-PRs`) used as persistent JSON storage between scheduled workflow runs. Avoids depending on external infrastructure. -- **flaky-test-detector** — A repository tool / Copilot skill that confirms a test is flaky by checking for failure evidence across ≥3 distinct unrelated PRs (not the originating PR). -- **Cat A/B/C** (regression-pr-shepherd) — Triage categories for regression-test PRs. **A**: new human review feedback since last bot comment. **B**: CI failure or merge conflict. **C**: healthy (skip this round). -- **B0–B4** (regression-pr-shepherd, Category B subtypes) — **B0**: merge conflict (rebase + resolve). **B1**: infrastructure/flaky failure (retry CI). **B2**: test compilation or setup error (fix test code). **B3**: added test fails — bug NOT fixed (remove label + comment + close PR). **B4**: other failures (note + re-trigger). -- **`.lock.yml`** — The compiled GitHub Actions YAML produced by `gh aw compile` from a gh-aw `.md` source. Checked in and executed by GitHub Actions directly; never hand-edited. -- **`BSL` (baseline)** — F# compiler test baseline files (`.bsl`); tests diff against these. `BSL auto-accept` means automatically regenerating baselines instead of comparing — disallowed in some auto-resolve paths because it masks regressions. -- **`dotnet/skills`** — A separate Microsoft repository hosting reusable Copilot skills (validators, tools, agent prompts). Some workflows (e.g., `skill-validation.yml`) download nightly-release binaries from there. -- **`FCS` (F# Compiler Service)** — The F# compiler-as-a-library used by IDEs and tools. "FCS-testable" issues can be reproduced/fixed via the compiler service without needing Visual Studio. -- **`has_ci`** (labelops-pr-maintenance) — PR has CI runs to evaluate. -- **`has_conflicts`** (labelops-pr-maintenance) — PR has merge conflicts. -- **`ci_blocked`** (labelops-pr-maintenance) — CI hasn't started yet (queued or blocked by other workflows). -- **`12h stuck guard`** (labelops-pr-maintenance) — Skip the PR if LabelOps already committed within the last 12 hours AND checks are still red. Prevents retry storms. -- **`messages` safe-output** — gh-aw runtime mechanism for posting lifecycle status updates (run-started, run-success, run-failure) and a per-run footer to a user-visible target (issue/PR comment). Templates support placeholders like `{workflow_name}`, `{run_url}`, `{event_type}`. Not rate-limited via `max:` — it's a per-run signaling channel, not a content output. -- **milestone 29 / `2026-05-12` cutoff** — Repo-specific operational constants: milestone 29 is applied by `add_to_project.yml` (internal milestone number; title unconfirmed from source); the `2026-05-12` cutoff in `labelops-pr-security-scan` is the date the scanner went live — PRs opened before that date are skipped to avoid re-scanning historical PRs. -- **`⚠️ Affects-*` family** (labelops-pr-security-scan) — agent-applied warning labels indicating which repository area a PR touches: `Build-Infra`, `Compiler-Output`, `Bootstrap`, `Restore`, `Design-Time`, `Test-Tooling`, `Agent-Config`. Drive reviewer attention to PRs touching sensitive code paths. +- **gh-aw** — GitHub CLI extension (`gh aw`) compiling agentic `.md` workflows (frontmatter + prompt) into `.lock.yml` GitHub Actions files. +- **safe-outputs** — gh-aw's permission/rate-limit framework constraining agent side effects per run (`max:`, `allowed-files:`, `labels:`). +- **noop** — Safe-output ending a run with no side effects. `report-as-issue: false` = silent no-op. +- **CCA (Copilot Coding Agent)** — Hosted coding agent invoked via `create-agent-session`. +- **state-store branch** — Git branch (e.g., `memory/repo-assist`, `safety/scanned-PRs`) for persistent JSON storage between runs. +- **flaky-test-detector** — Skill confirming flaky tests via ≥3 distinct PR failure evidence. +- **Cat A/B/C** (RPS) — Regression PR triage: A = review feedback, B = CI/conflict, C = healthy. +- **B0–B4** (RPS Cat B) — B0: conflict, B1: infra/flaky, B2: compile error, B3: bug NOT fixed, B4: other. +- **FCS** — F# Compiler Service (compiler-as-library for IDEs). +- **`.lock.yml`** — Compiled Actions YAML from `gh aw compile`; never hand-edited. ## Legend -| Symbol | Meaning | -|---|---| -| ⏰ | scheduled trigger (cron) | -| 👤 | human-initiated trigger (manual dispatch, PR/issue events, comment, reaction) | -| ⚙️ | workflow-engine action (job condition, step logic, push event, internal evaluation) | -| 🤖 | agent/bot action (safe-output emission, dispatch, autonomous write) | -| `<>` | binary branch on a guard condition | -| `<>` / `<>` | parallel job split / join | - ---- +| Prefix | Meaning | +|--------|---------| +| ⏰ | Cron/schedule trigger | +| 👤 | Human-initiated (PR, issue, comment, dispatch) | +| ⚙️ | Workflow engine (job condition, step logic, push) | +| 🤖 | Bot/agent action | +| `<>` | Binary branch (exactly 2 outgoing edges) | +| `<>`/`<>` | Parallel execution (overlapping guards) | ## Overview -| Workflow | Trigger | Inputs | Primary Actions | -|---|---|---|---| -| `agentic-state-machine.md` | schedule 7d, dispatch | none | `noop`, `create-pull-request` | -| `aw-auto-update.md` | schedule 24h, dispatch | none | `noop`, `create-agent-session` | -| `labelops-flake-fix.md` | dispatch | `failing_test`, `affected_prs`, `originating_pr` (all req) | `create-pull-request`, `add-comment`, `create-issue` | -| `labelops-pr-maintenance.md` | schedule 3h, dispatch | none | `noop`, `add-comment`, `push-to-pull-request-branch`, `add-labels`, `dispatch-workflow` | -| `labelops-pr-security-scan.md` | schedule 1h, dispatch | none | `noop`, `add-labels`, `add-comment` | -| `regression-pr-shepherd.md` | schedule 4h, dispatch | none | `noop`, `add-comment`, `push-to-pull-request-branch`, `remove-labels` | -| `repo-assist.md` | schedule 12h, dispatch, slash_command `/repo-assist`, reaction `eyes` | none | `noop`, `messages`, `add-comment`, `create-pull-request`, `push-to-pull-request-branch`, `create-issue`, `update-issue`, `add-labels`, `remove-labels` | -| `add_to_project.yml` | issues (opened, transferred); pull_request_target (opened, main — gated off) | — | add label `Needs-Triage`, set milestone 29 | -| `backport.yml` | issue_comment (created), schedule (cron `0 13 * * *`) | — | delegates to `dotnet/arcade` backport-base.yml | -| `branch-merge.yml` | push (main, release/\*) | — | delegates to `dotnet/arcade` inter-branch-merge-base.yml | -| `check_release_notes.yml` | pull_request_target (opened/sync/reopened/labeled/unlabeled; main, release/\*) | — | create or update PR comment | -| `commands.yml` | issue_comment (created) | — | apply patch to PR branch, comment on PR | -| `copilot-setup-steps.yml` | dispatch | none | build environment setup for Copilot agent | -| `repository_lockdown_check.yml` | pull_request_target (opened/sync/reopened; main, release/\*) | — | create, update, or delete lockdown comment | -| `skill-validation.yml` | pull_request (`.github/skills/**`, `.github/agents/**`), push (main), dispatch | none | validate skills and agents | - -### Operational limits - -| Workflow | Timeout | Concurrency | -|---|---|---| -| `labelops-pr-maintenance.md` | 90 min | `labelops-pr-maintenance`, cancel-in-progress: false | -| `labelops-flake-fix.md` | 60 min | `labelops-flake-fix-${{inputs.failing_test}}`, cancel-in-progress: false | -| `labelops-pr-security-scan.md` | 15 min | `labelops-pr-security-scan`, cancel-in-progress: false | -| `repo-assist.md` | 60 min | (per-issue/PR, default) | -| `regression-pr-shepherd.md` | 30 min | (default per-run) | -| `skill-validation.yml` | (Actions default) | `skill-validation-${{pr#||ref}}`, cancel-in-progress: true | - -> **Defaults** for unlisted gh-aw workflows: `agentic-state-machine.md`, `aw-auto-update.md` — 15 min timeout, no serialization. Classic `.yml` workflows use GitHub Actions defaults unless noted. - ---- +| # | Workflow | Trigger | Inputs | Primary Actions | +|---|---------|---------|--------|-----------------| +| 1 | `agentic-state-machine.md` | ⏰ weekly, 👤 dispatch | none | create-pull-request (.github/docs/**) | +| 2 | `aw-auto-update.md` | 👤 dispatch | none | noop or create-agent-session | +| 3 | `backport.yml` | 👤 issue_comment, ⏰ daily | none | reusable workflow (dotnet/arcade) | +| 4 | `branch-merge.yml` | ⚙️ push (release/*, main) | none | reusable workflow (dotnet/arcade) | +| 5 | `check_release_notes.yml` | 👤 pull_request_target | none | PR comment (release notes check) | +| 6 | `commands.yml` | 👤 issue_comment | none | run CLI, apply patch, comment | +| 7 | `copilot-setup-steps.yml` | 👤 dispatch | none | setup environment (checkout, build, tools) | +| 8 | `add_to_project.yml` | 👤 issues/PR opened | none | add label, set milestone, cleanup runs | +| 9 | `labelops-flake-fix.md` | 👤 dispatch | failing_test, affected_prs, originating_pr | create-pull-request, create-issue, add-comment | +| 10 | `labelops-pr-maintenance.md` | ⏰ every 3h, 👤 dispatch | none | push-to-PR, add-comment, add-labels, dispatch-workflow | +| 11 | `labelops-pr-security-scan.md` | ⏰ hourly, 👤 dispatch | none | add-labels, add-comment, repo-memory write | +| 12 | `msbuild-quality-review.md` | ⏰ weekly, 👤 dispatch | none | create-issue, create-pull-request (draft) | +| 13 | `regression-pr-shepherd.md` | ⏰ every 4h, 👤 dispatch | none | push-to-PR, add-comment, remove-labels | +| 14 | `repo-assist.md` | ⏰ every 12h, 👤 dispatch, 👤 slash_command | none | create-pull-request, add-comment, add/remove-labels, create/update-issue, push-to-PR | +| 15 | `repository_lockdown_check.yml` | 👤 pull_request_target | none | PR comment (lockdown warning) | +| 16 | `skill-validation.yml` | 👤 PR, ⚙️ push (main), 👤 dispatch | none | validate skills/agents | ## Handover Map -> **Cross-workflow signals at a glance.** Workflows talk to each other via labels, dispatch, indirect handoffs, and state-store branches. This map shows every cross-workflow signal documented below. - -| Source Workflow | Signal | Target | Mechanism | Notes | -|---|---|---|---|---| -| `labelops-pr-maintenance.md` | Proven flake (flaky-test-detector ≥3 distinct unrelated PRs; test not introduced by current PR) | `labelops-flake-fix.md` | `dispatch-workflow: workflows: [labelops-flake-fix]` | Passes inputs; max 3/run | -| `aw-auto-update.md` | CHANGED_FILES non-empty after `gh aw upgrade + compile` | Copilot Coding Agent (CCA) | `create-agent-session` (base: main, max: 1) | CCA writes `.lock.yml` files using `COPILOT_GITHUB_TOKEN` | -| `agentic-state-machine.md` | State-machine doc changed | PR reviewer (human) | `create-pull-request` (allowed-files: .github/docs/**) | Writes `.github/docs/state-machine.md` | -| `repo-assist.md` | Regression test PR created (Task 2) | `regression-pr-shepherd.md` | Indirect via label `AI-Issue-Regression-PR` on PR | Shepherd picks up in subsequent scheduled run | -| `commands.yml` | `/run ` approved PR comment | PR branch | `git push origin HEAD:branch` (direct write) | Requires commenter admin/write access | -| `backport.yml` | `/backport to ` PR comment | `dotnet/arcade` backport-base.yml | `uses: dotnet/arcade/.github/workflows/backport-base.yml@main` | Reusable workflow; schedule trigger only cleans old runs | -| `branch-merge.yml` | Push to `release/*` or `main` | `dotnet/arcade` inter-branch-merge-base.yml | `uses: dotnet/arcade/.github/workflows/inter-branch-merge-base.yml@main` | Config: `.config/service-branch-merge.json` | - ---- - -## Group A1 — Agentic Infrastructure - -```mermaid -stateDiagram-v2 - direction LR - state "agentic-state-machine" as ASM { - direction LR - [*] --> ASM_ReadManifest : ⏰ schedule (every 7d) - [*] --> ASM_ReadManifest : 👤 workflow_dispatch (inputs: none) - state ASM_ModeCheck <> - ASM_ReadManifest --> ASM_ModeCheck : ⚙️ mode detection via manifest header (generator SHA + source SHAs) - ASM_ModeCheck --> ASM_Noop : ⚙️ NOOP — INCREMENTAL + all SHAs unchanged - ASM_ModeCheck --> ASM_Generate : ⚙️ FULL_REWRITE or changed SHAs - ASM_Noop --> [*] : ⚙️ noop (report-as-issue: false) - ASM_Generate --> ASM_AuditPassed : ⚙️ build model, draft diagrams, run all structural + behavioral audits - state ASM_AuditPassed <> - ASM_AuditPassed --> ASM_Noop : ⚙️ audit failed — abort, no PR - ASM_AuditPassed --> ASM_Validate : ⚙️ audit passed - ASM_Validate --> [*] : 🤖 create-pull-request - } - state "aw-auto-update" as AWU { - direction LR - [*] --> AWU_Install : ⏰ schedule (every 24h) - [*] --> AWU_Install : 👤 workflow_dispatch (inputs: none) - state AWU_Installed <> - AWU_Install --> AWU_Installed : ⚙️ gh extension install github/gh-aw - AWU_Installed --> AWU_Noop : ⚙️ install failed - AWU_Installed --> AWU_Upgrade : ⚙️ installed - state AWU_Upgraded <> - AWU_Upgrade --> AWU_Upgraded : ⚙️ gh aw upgrade - AWU_Upgraded --> AWU_Noop : ⚙️ upgrade failed - AWU_Upgraded --> AWU_Compile : ⚙️ upgraded - state AWU_Compiled <> - AWU_Compile --> AWU_Compiled : ⚙️ gh aw compile - AWU_Compiled --> AWU_Noop : ⚙️ compile errors - AWU_Compiled --> AWU_Capture : ⚙️ compiled - AWU_Capture --> AWU_Reset : ⚙️ capture NEW_VERSION + DIFF_STAT + CHANGED_FILES - AWU_Reset --> AWU_Dedupe : ⚙️ git reset --hard && git clean -fd - state AWU_DupeExists <> - AWU_Dedupe --> AWU_DupeExists : ⚙️ gh pr list + gh issue list (title: "[Auto Update] Agentic workflows") - AWU_DupeExists --> AWU_Noop : ⚙️ open PR or issue found - AWU_DupeExists --> AWU_Decide : ⚙️ none found - state AWU_HasChanges <> - AWU_Decide --> AWU_HasChanges : ⚙️ CHANGED_FILES empty? - AWU_HasChanges --> AWU_Noop : ⚙️ empty (normal steady state) - AWU_HasChanges --> AWU_Session : ⚙️ non-empty — delegate to CCA - AWU_Session --> [*] : 🤖 create-agent-session (to CCA) - AWU_Noop --> [*] : ⚙️ noop (report-as-issue: false) - } -``` - -### Safe-outputs configuration - -**gh-aw safe-output defaults (suppressed below):** `target: "*"`, `noop.report-as-issue: false`. Per-workflow blocks list overrides and distinguishing config. **`noop` rows omitted — all gh-aw workflows emit `noop | — | (defaults)`.** - -| Workflow | Output | Max | Key Constraints | -|---|---|---|---| -| `agentic-state-machine.md` | `create-pull-request` | 1 | title `[Agentic State Machine] `; allowed-files `.github/docs/**`; protected-files allowed | -| `aw-auto-update.md` | `create-agent-session` | 1 | base `main` | - ---- - -## Group A2 — LabelOps Agents +Cross-workflow interactions (producer → consumer): -```mermaid -stateDiagram-v2 - direction LR - state "labelops-flake-fix" as LFF { - direction LR - [*] --> LFF_Validate : 👤 workflow_dispatch (inputs: failing_test, affected_prs, originating_pr) - state LFF_InputOK <> - LFF_Validate --> LFF_InputOK : ⚙️ validate inputs - LFF_InputOK --> [*] : ⚙️ invalid inputs — exit - LFF_InputOK --> LFF_Reverify : ⚙️ valid - state LFF_Confirmed <> - LFF_Reverify --> LFF_Confirmed : ⚙️ flaky-test-detector: evidence across ≥3 of affected_prs? - LFF_Confirmed --> LFF_SkipExplain : ⚙️ not confirmed - LFF_Confirmed --> LFF_CheckAuthor : ⚙️ confirmed - state LFF_AuthorIntro <> - LFF_CheckAuthor --> LFF_AuthorIntro : ⚙️ originating PR introduced or modified this test (gh pr diff)? - LFF_AuthorIntro --> LFF_SkipExplain : ⚙️ yes — skip (would defeat PR purpose) - LFF_AuthorIntro --> LFF_Reproduce : ⚙️ no - LFF_Reproduce --> LFF_AllFail : ⚙️ reproduce loop up to 20 iterations, 15 min cap - state LFF_AllFail <> - LFF_AllFail --> LFF_SkipExplain : ⚙️ N/N failures — hard failure, not a flake - LFF_AllFail --> LFF_AnyFail : ⚙️ not all failed - state LFF_AnyFail <> - LFF_AnyFail --> LFF_Quarantine : ⚙️ 0/N — no local repro, prefer quarantine (Option B) - LFF_AnyFail --> LFF_DetermFix : ⚙️ 1-(N-1)/N — classic non-determinism, prefer fix (Option A) - LFF_DetermFix --> LFF_OpenPR : ⚙️ root cause fixed, 0/20 loop verified - LFF_Quarantine --> LFF_TrackingIssue : ⚙️ add skip marker referencing tracking issue - LFF_TrackingIssue --> LFF_OpenPR : 🤖 create-issue (labels: Flaky, automation, max: 1) - LFF_OpenPR --> LFF_Comment : 🤖 create-pull-request (fix) - LFF_Comment --> [*] : 🤖 add-comment on originating PR (max: 1) - LFF_SkipExplain --> [*] : 🤖 add-comment explaining the skip reason on originating PR - } - state "labelops-pr-maintenance" as LPM { - direction LR - [*] --> LPM_SelectPRs : ⏰ schedule (every 3h) - [*] --> LPM_SelectPRs : 👤 workflow_dispatch (inputs: none) - state LPM_HasPRs <> - LPM_SelectPRs --> LPM_HasPRs : ⚙️ gh pr list (label AI-Auto-Resolve-*, ≤3) - LPM_HasPRs --> LPM_Noop : ⚙️ no eligible PRs - LPM_HasPRs --> LPM_ClassifyPR : ⚙️ up to 3 PRs selected - LPM_ClassifyPR --> LPM_CICheck : ⚙️ classify (has_ci/has_conflicts/ci_blocked, 12h stuck guard) - state LPM_NeedsCITriage <> - LPM_CICheck --> LPM_NeedsCITriage : ⚙️ has_ci AND NOT ci_blocked? - LPM_NeedsCITriage --> LPM_CITriage : ⚙️ yes - LPM_NeedsCITriage --> LPM_ConflictCheck : ⚙️ no - state LPM_CIHealthy <> - LPM_CITriage --> LPM_CIHealthy : ⚙️ all checks SUCCESS/SKIPPED/NEUTRAL? - LPM_CIHealthy --> LPM_ConflictCheck : ⚙️ yes — healthy - LPM_CIHealthy --> LPM_CIFixable : ⚙️ no — failures exist - state LPM_CIFixable <> - LPM_CIFixable --> LPM_Fix : ⚙️ fixable (≤3 attempts, ≤500 LOC, no BSL auto-accept) - LPM_CIFixable --> LPM_ProvenFlake : ⚙️ not fixable - LPM_Fix --> LPM_PushCIFix : ⚙️ reproduce locally + fix + build + targeted tests - LPM_PushCIFix --> LPM_NextPR : 🤖 push-to-pull-request-branch + add-comment — stop this PR (CI restarts) - state LPM_ProvenFlake <> - LPM_ProvenFlake --> LPM_ExistingFlakePRCheck : ⚙️ flaky-test-detector >=3 distinct PRs AND test not introduced by this PR - LPM_ProvenFlake --> LPM_Escalate : ⚙️ not proven flake - state LPM_ExistingFlakePRCheck <> - LPM_ExistingFlakePRCheck --> LPM_NextPR : ⚙️ existing [LabelOps Flake] PR found (skip) - LPM_ExistingFlakePRCheck --> LPM_DispatchFlakeFix : ⚙️ no existing [LabelOps Flake] PR - LPM_DispatchFlakeFix --> LPM_ConflictCheck : 🤖 dispatch-workflow: labelops-flake-fix (max: 3) - LPM_Escalate --> LPM_ConflictCheck : 🤖 add-labels AI-needs-CI-fix-input + add-comment - state LPM_NeedsConflictTriage <> - LPM_ConflictCheck --> LPM_NeedsConflictTriage : ⚙️ has_conflicts AND Step 3 did NOT push? - LPM_NeedsConflictTriage --> LPM_NextPR : ⚙️ no conflict work needed - LPM_NeedsConflictTriage --> LPM_MergeTree : ⚙️ yes - state LPM_ConflictsExist <> - LPM_MergeTree --> LPM_ConflictsExist : ⚙️ git merge-tree --write-tree --messages origin/main HEAD: any CONFLICT lines? - LPM_ConflictsExist --> LPM_NextPR : ⚙️ no CONFLICT lines — PR merges cleanly - LPM_ConflictsExist --> LPM_Resolve : ⚙️ conflicts exist - LPM_Resolve --> LPM_NextPR : 🤖 push-to-pull-request-branch + add-comment - state LPM_MorePRs <> - LPM_NextPR --> LPM_MorePRs : ⚙️ more PRs remaining in batch (max 3)? - LPM_MorePRs --> LPM_ClassifyPR : ⚙️ yes (next PR) - LPM_MorePRs --> LPM_Done : ⚙️ no - LPM_Done --> [*] : ⚙️ run complete - LPM_Noop --> [*] : ⚙️ noop (report-as-issue: false) - } - state "labelops-pr-security-scan" as LPSS { - direction LR - [*] --> LPSS_ReadRules : ⏰ schedule (every 1h) - [*] --> LPSS_ReadRules : 👤 workflow_dispatch (inputs: none) - LPSS_ReadRules --> LPSS_ReadMemory : ⚙️ get_file_contents .github/tooling-check-repo-rules.md from default branch - LPSS_ReadMemory --> LPSS_ListPRs : ⚙️ load state.json from safety/scanned-PRs branch - state LPSS_HasPRs <> - LPSS_ListPRs --> LPSS_HasPRs : ⚙️ paginate PRs (newest-first, skip isDraft, cutoff 2026-05-12) - LPSS_HasPRs --> LPSS_WriteMemory : ⚙️ no PRs to scan - LPSS_HasPRs --> LPSS_PruneMemory : ⚙️ PRs found - LPSS_PruneMemory --> LPSS_PerPR : ⚙️ remove closed PRs from state.json - state LPSS_AlreadyScanned <> - LPSS_PerPR --> LPSS_AlreadyScanned : ⚙️ state.json sha == current headRefOid? - LPSS_AlreadyScanned --> LPSS_NextPR : ⚙️ yes — already scanned at this commit - LPSS_AlreadyScanned --> LPSS_ForkCheck : ⚙️ no — scan needed - state LPSS_IsFork <> - LPSS_ForkCheck --> LPSS_IsFork : ⚙️ headRepository field: is fork? - LPSS_IsFork --> LPSS_Bypass : ⚙️ non-fork - LPSS_IsFork --> LPSS_Classify : ⚙️ fork — read file list + diff + title + body - LPSS_Bypass --> LPSS_NextPR : 🤖 add-labels: AI-Tooling-Check-Bypassed + update memory (no comment) - state LPSS_Flagged <> - LPSS_Classify --> LPSS_Flagged : ⚙️ classify against generic + repo-specific categories - LPSS_Flagged --> LPSS_ApplyClean : ⚙️ no categories matched - LPSS_Flagged --> LPSS_CatChanged : ⚙️ categories matched - LPSS_ApplyClean --> LPSS_NextPR : 🤖 add-labels: AI-Tooling-Check-Scanned-Clean + update memory (no comment) - state LPSS_CatChanged <> - LPSS_CatChanged --> LPSS_FlagsAndComment : ⚙️ category set changed or no previous entry - LPSS_CatChanged --> LPSS_FlagsOnly : ⚙️ category set identical to previous scan - LPSS_FlagsAndComment --> LPSS_NextPR : 🤖 add-labels (warning labels) + add-comment (hide) + update memory - LPSS_FlagsOnly --> LPSS_NextPR : 🤖 add-labels (warning labels) + update memory (no new comment) - state LPSS_MorePRs <> - LPSS_NextPR --> LPSS_MorePRs : ⚙️ more PRs? - LPSS_MorePRs --> LPSS_PerPR : ⚙️ yes (next PR) - LPSS_MorePRs --> LPSS_WriteMemory : ⚙️ no - LPSS_WriteMemory --> [*] : ⚙️ save state.json to safety/scanned-PRs branch - } - LPM_DispatchFlakeFix --> LFF_Validate : 🤖 dispatch-workflow (cross-workflow, passes inputs) -``` +| Signal | Producer | Consumer | Mechanism | +|--------|----------|----------|-----------| +| `AI-Auto-Resolve-CI/Conflicts` labels | Human maintainer | `labelops-pr-maintenance` | Label filter on PR list | +| `AI-Issue-Regression-PR` label | `repo-assist` | `regression-pr-shepherd` | Label filter on PR list | +| `AI-thinks-issue-fixed` label | `repo-assist` | `regression-pr-shepherd` (remove) | Label on linked issue | +| `dispatch-workflow: labelops-flake-fix` | `labelops-pr-maintenance` | `labelops-flake-fix` | workflow_dispatch with inputs | +| `Flaky` label | `labelops-flake-fix` | Human triage | always-applied on PR/issue | +| `AI-needs-CI-fix-input` label | `labelops-pr-maintenance` | Human maintainer | escalation signal | +| `⚠️ Affects-*` labels | `labelops-pr-security-scan` | Human reviewer | informational | +| `Needs-Triage` label | `add_to_project.yml` | Human triage | imperative on new issues | +| State-store `safety/scanned-PRs` | `labelops-pr-security-scan` | `labelops-pr-security-scan` | repo-memory persistence | +| State-store `memory/repo-assist` | `repo-assist` | `repo-assist` | repo-memory persistence | -### Safe-outputs configuration +## Group A — LabelOps Ecosystem -| Workflow | Output | Max | Key Constraints | -|---|---|---|---| -| `labelops-flake-fix.md` | `create-pull-request` | 1 | title `[LabelOps Flake] `; labels `automation, Flaky, NO_RELEASE_NOTES`; protected-files fallback-to-issue | -| `labelops-flake-fix.md` | `add-comment` | 1 | (defaults) | -| `labelops-flake-fix.md` | `create-issue` | 1 | title `[LabelOps Flake] `; labels `Flaky, automation` | -| `labelops-pr-maintenance.md` | `add-comment` | 5 | hide-older-comments | -| `labelops-pr-maintenance.md` | `push-to-pull-request-branch` | 5 | protected-files allowed | -| `labelops-pr-maintenance.md` | `add-labels` | 3 | allowed `AI-needs-CI-fix-input` | -| `labelops-pr-maintenance.md` | `dispatch-workflow` | 3 | workflows `[labelops-flake-fix]` | -| `labelops-pr-security-scan.md` | `add-labels` | 50 | allowed: 11 security labels (see Labels) | -| `labelops-pr-security-scan.md` | `add-comment` | 25 | hide-older-comments | +Workflows: `labelops-pr-maintenance` (LPM), `labelops-flake-fix` (LFF), `labelops-pr-security-scan` (LPSS). ---- - -## Group A3 — Code Quality Agents +LPM dispatches LFF when proven flakes are detected. LPSS operates independently on a separate schedule scanning fork PRs. ```mermaid stateDiagram-v2 - direction LR - state "regression-pr-shepherd" as RPS { - direction LR - [*] --> RPS_ListPRs : ⏰ schedule (every 4h) - [*] --> RPS_ListPRs : 👤 workflow_dispatch (inputs: none) - state RPS_HasEligible <> - RPS_ListPRs --> RPS_HasEligible : ⚙️ gh pr list (label AI-Issue-Regression-PR, title filter) - RPS_HasEligible --> RPS_Noop : ⚙️ no eligible PRs - RPS_HasEligible --> RPS_QuickTriage : ⚙️ up to 3 eligible PRs (priority: Cat A > Cat B > Cat C) - RPS_QuickTriage --> RPS_HasFeedback : ⚙️ quick triage: mergeable + checks + memory - state RPS_HasFeedback <> - RPS_HasFeedback --> RPS_FixFeedback : ⚙️ new human review comments since last bot comment? => Category A - RPS_HasFeedback --> RPS_HasCIFailure : ⚙️ no new review feedback - state RPS_HasCIFailure <> - RPS_HasCIFailure --> RPS_CITriage : ⚙️ yes => Category B (CI failure or merge conflict) - RPS_HasCIFailure --> RPS_NextPR : ⚙️ no => Category C (healthy, skip) - RPS_FixFeedback --> RPS_SameFixCheck : ⚙️ check: last commit matches feedback? - state RPS_SameFixCheck <> - RPS_SameFixCheck --> RPS_NextPR : ⚙️ already pushed (last commit matches feedback — skip) - RPS_SameFixCheck --> RPS_PushFix : ⚙️ not yet pushed - RPS_PushFix --> RPS_NextPR : 🤖 push-to-pull-request-branch + add-comment (reply to review thread) - RPS_CITriage --> RPS_B0Conflict : ⚙️ fetch failed job logs, analyze failure type - state RPS_B0Conflict <> - RPS_B0Conflict --> RPS_RebaseResolve : ⚙️ yes => B0: rebase + resolve (tests/ scope) - RPS_B0Conflict --> RPS_B1Infra : ⚙️ no - state RPS_B1Infra <> - RPS_B1Infra --> RPS_Retry : ⚙️ yes => B1: infrastructure or flaky failure — retry CI - RPS_B1Infra --> RPS_B2Error : ⚙️ no - state RPS_B2Error <> - RPS_B2Error --> RPS_FixTest : ⚙️ yes => B2: test compilation or setup error — fix test code - RPS_B2Error --> RPS_B3BugReproduced : ⚙️ no - state RPS_B3BugReproduced <> - RPS_B3BugReproduced --> RPS_BugStillExists : ⚙️ yes => B3: added test fails (bug NOT fixed) - RPS_B3BugReproduced --> RPS_B4Other : ⚙️ no => B4: other failures (note + re-trigger) - RPS_RebaseResolve --> RPS_NextPR : 🤖 push-to-pull-request-branch + add-comment - RPS_Retry --> RPS_NextPR : 🤖 push empty commit or re-run workflow - RPS_FixTest --> RPS_NextPR : 🤖 push-to-pull-request-branch + add-comment - RPS_B4Other --> RPS_NextPR : 🤖 add-comment - RPS_BugStillExists --> RPS_NextPR : 🤖 remove-labels AI-thinks-issue-fixed + add-comment + close PR - state RPS_MorePRs <> - RPS_NextPR --> RPS_MorePRs : ⚙️ more PRs in batch (max 3)? - RPS_MorePRs --> RPS_QuickTriage : ⚙️ yes (next PR) - RPS_MorePRs --> RPS_Done : ⚙️ no - RPS_Done --> [*] : ⚙️ run complete, update memory - RPS_Noop --> [*] : ⚙️ noop (report-as-issue: false) - } - state "repo-assist" as RA { - direction LR - [*] --> RA_PreStep : ⏰ schedule (every 12h) - [*] --> RA_PreStep : 👤 workflow_dispatch (inputs: none) - [*] --> RA_PreStep : 👤 slash_command: /repo-assist - [*] --> RA_PreStep : 👤 reaction: eyes - RA_PreStep --> RA_ReadMemory : ⚙️ fetch issues + PRs, compute weights, write task_selection.json - RA_ReadMemory --> RA_CommandMode : ⚙️ read state.json from memory/repo-assist branch - state RA_CommandMode <> - RA_CommandMode --> RA_RunInstructions : 👤 instructions non-empty (slash_command or reaction with text) - RA_CommandMode --> RA_T1 : ⚙️ instructions empty — non-command mode - state RA_InstructionResult <> - RA_RunInstructions --> RA_InstructionResult : ⚙️ follow user instructions, apply guidelines - RA_InstructionResult --> [*] : ⚙️ no actionable work => noop - RA_InstructionResult --> RA_CmdOutputs : ⚙️ work done - RA_CmdOutputs --> [*] : 🤖 any of repo-assist's 9 safe-outputs (8 in table + noop) - RA_T1 --> RA_T3 : ⚙️ Task 1: investigate issues, add AI-thinks-issue-fixed/windows-only - RA_T3 --> RA_T2 : ⚙️ Task 3: revisit AI-thinks-windows-only, remove-labels if FCS-testable - RA_T2 --> RA_T2_SkipCheck : ⚙️ Task 2: Step A — check 6 skip conditions - state RA_T2_SkipCheck <> - RA_T2_SkipCheck --> RA_TaskFinal : ⚙️ skip (closed|existing-PR|test-coverage|untestable|human-coverage) - RA_T2_SkipCheck --> RA_T2_CreatePR : ⚙️ no skip condition met - state RA_T2_CreatePR - RA_T2_CreatePR --> RA_TaskFinal : 🤖 create-pull-request (auto-merge, reviewers: abonie+T-Gro) or remove-labels - RA_TaskFinal --> RA_WriteMemory : ⚙️ Task FINAL: update Monthly Activity Summary issue (update-issue) - RA_WriteMemory --> [*] : 🤖 write state.json to memory/repo-assist branch, messages safe-output - } - RA_T2_CreatePR --> RPS_ListPRs : 🤖 async via label AI-Issue-Regression-PR (picked up in next RPS run) + direction LR + + state "labelops-pr-maintenance" as LPM { + [*] --> LPM_SelectPRs : ⏰ every 3h / 👤 dispatch + LPM_SelectPRs --> LPM_NoPRs : ⚙️ no eligible PRs + LPM_NoPRs --> [*] + LPM_SelectPRs --> LPM_Classify : ⚙️ ≤3 PRs selected + LPM_Classify --> LPM_CICheck : ⚙️ has_ci AND NOT ci_blocked + LPM_Classify --> LPM_Conflicts : ⚙️ has_conflicts only + state LPM_CIHealthy <> + LPM_CICheck --> LPM_CIHealthy : ⚙️ evaluate CI status + LPM_CIHealthy --> LPM_Conflicts : ⚙️ all healthy + LPM_CIHealthy --> LPM_CIFixable : ⚙️ not healthy + state LPM_CIFixable <> + LPM_CIFixable --> LPM_FixCI : ⚙️ fixable failure + LPM_FixCI --> [*] + LPM_CIFixable --> LPM_FlakeCheck : ⚙️ suspected flake + state LPM_FlakeCheck <> + LPM_FlakeCheck --> LPM_DispatchFlake : ⚙️ ≥3 PRs confirm + LPM_FlakeCheck --> LPM_Escalate : ⚙️ insufficient evidence + LPM_DispatchFlake --> [*] + LPM_Escalate --> [*] + state LPM_ConflictResult <> + LPM_Conflicts --> LPM_ConflictResult : ⚙️ merge-tree check + LPM_ConflictResult --> LPM_Resolve : ⚙️ conflicts exist + LPM_ConflictResult --> [*] : ⚙️ merges cleanly + LPM_Resolve --> [*] + } + + state "labelops-flake-fix" as LFF { + [*] --> LFF_Validate : 👤 dispatch (inputs) + LFF_Validate --> LFF_Reverify : ⚙️ inputs valid + state LFF_Confirmed <> + LFF_Reverify --> LFF_Confirmed : ⚙️ flake evidence check + LFF_Confirmed --> LFF_Reproduce : ⚙️ confirmed ≥3 PRs + LFF_Confirmed --> LFF_Noop : ⚙️ not reproducible + LFF_Noop --> [*] + state LFF_Strategy <> + LFF_Reproduce --> LFF_Strategy : ⚙️ local repro result + LFF_Strategy --> LFF_Fix : ⚙️ non-determinism (fix) + LFF_Strategy --> LFF_Quarantine : ⚙️ non-trivial (quarantine) + LFF_Fix --> LFF_PR : 🤖 create-pull-request + LFF_Quarantine --> LFF_PR : 🤖 create-pull-request + LFF_PR --> LFF_Comment : 🤖 add-comment (originating PR) + LFF_Comment --> [*] + } + + state "labelops-pr-security-scan" as LPSS { + [*] --> LPSS_ReadRules : ⏰ hourly / 👤 dispatch + LPSS_ReadRules --> LPSS_LoadMemory : ⚙️ read repo rules + LPSS_LoadMemory --> LPSS_ListPRs : ⚙️ load state.json + LPSS_ListPRs --> LPSS_PerPR : ⚙️ filter (date, draft, sha) + state LPSS_ForkCheck <> + LPSS_PerPR --> LPSS_ForkCheck : ⚙️ check headRepository + LPSS_ForkCheck --> LPSS_Bypass : ⚙️ non-fork + LPSS_ForkCheck --> LPSS_Classify : ⚙️ fork PR + LPSS_Bypass --> LPSS_SaveMemory : 🤖 add-labels (Bypassed) + LPSS_Classify --> LPSS_Label : 🤖 add-labels (⚠️ categories) + LPSS_Label --> LPSS_SaveMemory : 🤖 add-comment (if changed) + LPSS_SaveMemory --> [*] + } + + LPM_DispatchFlake --> LFF_Validate : 🤖 dispatch-workflow (cross-workflow) ``` -> **`repo-assist` Task 2 — Step A skip conditions** (any of these → no PR): -> 1. The issue is **closed** — someone already resolved it -> 2. A regression test PR exists (open or merged): `gh pr list --label "AI-Issue-Regression-PR" --search "{issue_number}" --state all` -> 3. The issue body or comments link to a PR that addresses it -> 4. Repo Assist already posted a test-link comment (a comment containing a GitHub permalink to a test file) -> 5. Repo Assist already posted an "untestable" explanation (Outcome 3 from a previous run) -> 6. A human already posted a comment with test coverage or a fix reference after the `AI-thinks-issue-fixed` label was applied - -> **`repo-assist` task ordering** — non-sequential by design: **Task 1 → Task 3 → Task 2 → Task FINAL**. Task 3 revisits `AI-thinks-windows-only` labels (which Task 1 may have just applied) BEFORE Task 2 attempts to write regression tests, because Task 3's findings determine whether Task 2 should skip. - -### Safe-outputs configuration +## Group B — Regression Test Pipeline -| Workflow | Output | Max | Key Constraints | -|---|---|---|---| -| `regression-pr-shepherd.md` | `add-comment` | 5 | hide-older-comments | -| `regression-pr-shepherd.md` | `push-to-pull-request-branch` | 10 | title `Add regression test: `; labels `AI-Issue-Regression-PR`; allowed-files `tests/**`, `vsintegration/tests/**`; protected-files fallback-to-issue | -| `regression-pr-shepherd.md` | `remove-labels` | 5 | allowed `AI-thinks-issue-fixed` | -| `repo-assist.md` | `messages` | n/a | lifecycle signaling — see Glossary | -| `repo-assist.md` | `add-comment` | 10 | hide-older-comments | -| `repo-assist.md` | `create-pull-request` | 10 | title `Add regression test: `; labels `NO_RELEASE_NOTES, AI-Issue-Regression-PR`; reviewers abonie, T-Gro; auto-merge; allowed-files `tests/**`, `vsintegration/tests/**` | -| `repo-assist.md` | `push-to-pull-request-branch` | 4 | title `[Repo Assist] `; protected-files fallback-to-issue | -| `repo-assist.md` | `create-issue` | 4 | title `[Repo Assist] `; labels `automation, repo-assist` | -| `repo-assist.md` | `update-issue` | 1 | title `[Repo Assist] ` | -| `repo-assist.md` | `add-labels` | 30 | allowed `AI-thinks-issue-fixed, AI-thinks-windows-only` | -| `repo-assist.md` | `remove-labels` | 10 | allowed `AI-thinks-issue-fixed, AI-thinks-windows-only` | - ---- +Workflows: `repo-assist` (RA), `regression-pr-shepherd` (RPS). -## Group B — PR & Issue Triage +RA creates regression test PRs and labels issues. RPS shepherds those PRs to merge. ```mermaid stateDiagram-v2 - direction LR - state "add_to_project" as ATP { - direction LR - [*] --> ATP_TriggerGuard : 👤 issues (opened / transferred) - [*] --> ATP_TriggerGuard : 👤 pull_request_target (opened, branches: main) - state ATP_IsIssueEvent <> - ATP_TriggerGuard --> ATP_IsIssueEvent : ⚙️ job guard: github.event_name != 'pull_request_target'? - ATP_IsIssueEvent --> [*] : ⚙️ false — all jobs skipped (pull_request_target gated off) - ATP_IsIssueEvent --> ATP_Fork : ⚙️ true (issues event) - state ATP_Fork <> - state ATP_Join <> - ATP_Fork --> ATP_CleanupRuns : ⚙️ cleanup_old_runs job (parallel) - ATP_Fork --> ATP_ApplyLabel : ⚙️ apply-label job (parallel) - ATP_Fork --> ATP_ApplyMilestone : ⚙️ apply-milestone job (parallel) - ATP_CleanupRuns --> ATP_Join : ⚙️ gh api DELETE all completed runs for this workflow - ATP_ApplyLabel --> ATP_Join : ⚙️ github.rest.issues.addLabels: Needs-Triage - ATP_ApplyMilestone --> ATP_Join : ⚙️ github.rest.issues.update: milestone=29 - ATP_Join --> [*] : ⚙️ all parallel jobs complete - } - state "check_release_notes" as CRN { - direction LR - [*] --> CRN_GetRef : 👤 pull_request_target (opened/sync/reopened/labeled/unlabeled, main, release/*) - CRN_GetRef --> CRN_Checkout : ⚙️ actions/github-script@v3: get PR head ref + repository - CRN_Checkout --> CRN_CheckNotes : ⚙️ actions/checkout@v2 (PR head ref, fetch-depth: 0) - CRN_CheckNotes --> CRN_FindComment : ⚙️ check release notes entries, NO_RELEASE_NOTES opt-out - CRN_FindComment --> CRN_CommentExists : ⚙️ find-comment (body: DO_NOT_REMOVE: release_notes_check) - state CRN_CommentExists <> - CRN_CommentExists --> CRN_CreateComment : ⚙️ comment-id == '' (no existing bot comment) - CRN_CommentExists --> CRN_UpdateComment : ⚙️ comment-id != '' (existing bot comment found) - CRN_CreateComment --> [*] : ⚙️ if: comment-id == '', createComment - CRN_UpdateComment --> [*] : ⚙️ if: comment-id != '', updateComment - } - state "repository_lockdown_check" as RLC { - direction LR - [*] --> RLC_CheckLockdown : 👤 pull_request_target (opened/synchronize/reopened, branches: main, release/*) - RLC_CheckLockdown --> RLC_FindComment : ⚙️ check vars.LOCKDOWN (exits 1 if "true") - RLC_FindComment --> RLC_CommentExists : ⚙️ find-comment (body: DO_NOT_REMOVE: repository_lockdown) - state RLC_CommentExists <> - RLC_CommentExists --> RLC_NewComment : ⚙️ comment-id == '' (no existing comment) - RLC_CommentExists --> RLC_OldComment : ⚙️ comment-id != '' (existing comment found) - state RLC_NewIsLocked <> - RLC_NewComment --> RLC_NewIsLocked : ⚙️ if: failure()? (lockdown was active) - RLC_NewIsLocked --> RLC_CreateComment : ⚙️ yes — lockdown active: create notice - RLC_NewIsLocked --> [*] : ⚙️ no — lockdown not active, no comment: nothing to do - state RLC_OldIsLocked <> - RLC_OldComment --> RLC_OldIsLocked : ⚙️ if: failure()? (lockdown still active) - RLC_OldIsLocked --> RLC_UpdateComment : ⚙️ yes — still locked: update notice - RLC_OldIsLocked --> RLC_DeleteComment : ⚙️ no — lockdown lifted: delete notice - RLC_CreateComment --> [*] : ⚙️ actions/github-script@v7: createComment (lockdown caution block) - RLC_UpdateComment --> [*] : ⚙️ actions/github-script@v7: updateComment (lockdown caution block) - RLC_DeleteComment --> [*] : ⚙️ actions/github-script@v7: deleteComment (lockdown lifted) - } + direction LR + + state "repo-assist" as RA { + [*] --> RA_FetchData : ⏰ every 12h / 👤 dispatch / 👤 slash_command + RA_FetchData --> RA_Task1 : ⚙️ task selection + RA_Task1 --> RA_Task3 : ⚙️ issue investigation + RA_Task3 --> RA_Task2 : ⚙️ windows-only revisit + state RA_TestResult <> + RA_Task2 --> RA_TestResult : ⚙️ regression test verification + RA_TestResult --> RA_CreatePR : ⚙️ test passes + RA_TestResult --> RA_RemoveLabel : ⚙️ test fails (bug not fixed) + RA_CreatePR --> RA_Final : 🤖 create-pull-request + RA_RemoveLabel --> RA_Final : 🤖 remove-labels (AI-thinks-issue-fixed) + RA_Final --> [*] + } + + state "regression-pr-shepherd" as RPS { + [*] --> RPS_ListPRs : ⏰ every 4h / 👤 dispatch + RPS_ListPRs --> RPS_Noop : ⚙️ no eligible PRs + RPS_Noop --> [*] + RPS_ListPRs --> RPS_Categorize : ⚙️ ≤3 PRs + state RPS_Category <> + RPS_Categorize --> RPS_Category : ⚙️ triage + RPS_Category --> RPS_Feedback : ⚙️ Cat A (review feedback) + RPS_Category --> RPS_CIFix : ⚙️ Cat B (CI/conflict) + RPS_Feedback --> RPS_Push : 🤖 push-to-pull-request-branch + RPS_Push --> [*] + state RPS_BType <> + RPS_CIFix --> RPS_BType : ⚙️ failure type + RPS_BType --> RPS_Rebase : ⚙️ B0 (conflict) + RPS_BType --> RPS_FixCode : ⚙️ B2 (compile error) + RPS_Rebase --> [*] + RPS_FixCode --> [*] + } + + RA_CreatePR --> RPS_ListPRs : 🤖 async via label AI-Issue-Regression-PR ``` ---- +## Group C — Traditional Workflows (PR Lifecycle) -## Group C — Comment & Slash Commands +Workflows triggered by PRs/issues: `check_release_notes`, `repository_lockdown_check`, `add_to_project`, `commands`, `backport`. ```mermaid stateDiagram-v2 - direction LR - state "commands" as CMD { - direction LR - [*] --> CMD_CheckAccess : 👤 issue_comment (created) - CMD_CheckAccess --> CMD_Allowed : ⚙️ authorize_commenter job: github-script getCollaboratorPermissionLevel - state CMD_Allowed <> - CMD_Allowed --> [*] : ⚙️ not authorized (admin/write) OR not on a PR — parsing_job if: guard false - CMD_Allowed --> CMD_ParseComment : ⚙️ allowed == 'true' AND issue.pull_request - CMD_ParseComment --> CMD_HasCmd : ⚙️ parse (/run fantomas|ilverify|xlf|test-baseline) - state CMD_HasCmd <> - CMD_HasCmd --> [*] : ⚙️ no recognized command — run-parsed-command if: guard false - CMD_HasCmd --> CMD_Checkout1 : ⚙️ command recognized - CMD_Checkout1 --> CMD_CheckoutPR1 : ⚙️ actions/checkout@v4 - CMD_CheckoutPR1 --> CMD_InstallDotnet : ⚙️ gh auth setup-git && gh pr checkout - CMD_InstallDotnet --> CMD_InstallTools : ⚙️ actions/setup-dotnet@v3 (global-json-file) - CMD_InstallTools --> CMD_IsTestBaseline : ⚙️ dotnet tool restore - state CMD_IsTestBaseline <> - CMD_IsTestBaseline --> CMD_SetupRuntime : ⚙️ command == '/run test-baseline' — actions/setup-dotnet@v4 (9.0.x) - CMD_IsTestBaseline --> CMD_RunCmd : ⚙️ other command — skip .NET 9 runtime setup - CMD_SetupRuntime --> CMD_RunCmd : ⚙️ .NET 9.0.x ready - CMD_RunCmd --> CMD_CreatePatch : ⚙️ run command (continue-on-error): fantomas/xlf/ilverify/test-baseline - CMD_CreatePatch --> CMD_UploadArtifacts : ⚙️ if: command, write run_step_outcome + hasPatch to result file - state "job boundary — cli-results artifact" as CMD_JobBoundary - CMD_UploadArtifacts --> CMD_JobBoundary : ⚙️ upload-artifact@v4 (cli-results) - CMD_JobBoundary --> CMD_RunSucceeded : ⚙️ apply-and-report job starts - state CMD_RunSucceeded <> - CMD_RunSucceeded --> [*] : ⚙️ run-parsed-command.result != 'success' — apply-and-report if: guard false - CMD_RunSucceeded --> CMD_Checkout2 : ⚙️ command != '' AND result == 'success' - CMD_Checkout2 --> CMD_CheckoutPR2 : ⚙️ actions/checkout@v4 - CMD_CheckoutPR2 --> CMD_Download : ⚙️ gh pr checkout - CMD_Download --> CMD_ReadMeta : ⚙️ actions/download-artifact@v4 (cli-results) - CMD_ReadMeta --> CMD_HasPatch : ⚙️ read run_step_outcome + hasPatch from result file - state CMD_HasPatch <> - CMD_HasPatch --> CMD_ValidatePaths : ⚙️ outcome == 'success' AND hasPatch == 'true' - CMD_HasPatch --> CMD_GenReport : ⚙️ outcome != 'success' OR hasPatch == 'false' - CMD_ValidatePaths --> CMD_ApplyPush : ⚙️ outcome==success && hasPatch: validate paths (src/ tests/ vsintegration/) - CMD_ApplyPush --> CMD_CountStats : ⚙️ outcome==success && hasPatch: patch + commit + push - CMD_CountStats --> CMD_GenReport : ⚙️ outcome==success && hasPatch: git diff stats - CMD_GenReport --> CMD_CommentPR : ⚙️ if: always, build markdown report => GITHUB_STEP_SUMMARY + pr_report.md - CMD_CommentPR --> [*] : ⚙️ if: always, gh pr comment --body-file pr_report.md - } - state "backport" as BKP { - direction LR - [*] --> BKP_TriggerGuard : 👤 issue_comment (created) - [*] --> BKP_TriggerGuard : ⏰ schedule (cron: 0 13 * * *) - state BKP_ShouldRun <> - BKP_TriggerGuard --> BKP_ShouldRun : ⚙️ if: contains(comment.body, '/backport to') OR event_name == 'schedule' - BKP_ShouldRun --> [*] : ⚙️ false — comment does not contain /backport to (and not schedule run) - BKP_ShouldRun --> BKP_Backport : ⚙️ true - BKP_Backport --> [*] : 🤖 uses: dotnet/arcade backport-base.yml@main (reusable workflow) - } + direction LR + + state "check_release_notes" as CRN { + [*] --> CRN_GetPR : 👤 pull_request_target (main, release/*) + CRN_GetPR --> CRN_CheckPaths : ⚙️ checkout PR ref + state CRN_Result <> + CRN_CheckPaths --> CRN_Result : ⚙️ check modified paths vs release notes + CRN_Result --> CRN_Pass : ⚙️ notes present OR NO_RELEASE_NOTES label + CRN_Result --> CRN_Fail : ⚙️ notes missing + CRN_Pass --> CRN_Comment : ⚙️ if: success() || failure() + CRN_Fail --> CRN_Comment : ⚙️ if: success() || failure() + CRN_Comment --> [*] + } + + state "repository_lockdown_check" as RLC { + [*] --> RLC_Check : 👤 pull_request_target (main, release/*) + state RLC_Locked <> + RLC_Check --> RLC_Locked : ⚙️ if: vars.LOCKDOWN == true + RLC_Locked --> RLC_Warn : ⚙️ true (lockdown active) + RLC_Locked --> RLC_Clean : ⚙️ false (no lockdown) + RLC_Warn --> [*] + RLC_Clean --> [*] + } + + state "add_to_project" as ATP { + [*] --> ATP_Entry : 👤 issues (opened, transferred) + state ATP_IsPR <> + [*] --> ATP_IsPR : 👤 pull_request_target (main, opened) + ATP_IsPR --> ATP_Entry : ⚙️ event != pull_request_target + ATP_IsPR --> [*] : ⚙️ event == pull_request_target (jobs gated off) + state ATP_Fork <> + state ATP_Join <> + ATP_Entry --> ATP_Fork : ⚙️ if: event != pull_request_target + ATP_Fork --> ATP_Label : 🤖 add Needs-Triage + ATP_Fork --> ATP_Milestone : 🤖 set milestone 29 + ATP_Fork --> ATP_Cleanup : 🤖 delete old workflow runs + ATP_Label --> ATP_Join + ATP_Milestone --> ATP_Join + ATP_Cleanup --> ATP_Join + ATP_Join --> [*] + } + + state "commands" as CMD { + [*] --> CMD_Auth : 👤 issue_comment (created) + state CMD_Allowed <> + CMD_Auth --> CMD_Allowed : ⚙️ check write access + CMD_Allowed --> [*] : ⚙️ not allowed + CMD_Allowed --> CMD_Parse : ⚙️ allowed + is PR + CMD_Parse --> CMD_Run : ⚙️ command matched + CMD_Run --> CMD_Apply : ⚙️ patch + validate paths + CMD_Apply --> CMD_Report : 🤖 push + comment + CMD_Report --> [*] + } + + state "backport" as BP { + [*] --> BP_Entry : 👤 issue_comment / ⏰ daily cron + state BP_Guard <> + BP_Entry --> BP_Guard : ⚙️ if: contains(body, '/backport to') OR schedule + BP_Guard --> BP_Run : ⚙️ true + BP_Guard --> [*] : ⚙️ false + BP_Run --> [*] + } ``` -> **`/run` commands** — available via PR comment by users with admin/write access: -> -> | Command | Tool | Effect | -> |---|---|---| -> | `/run fantomas` | F# code formatter | applies `dotnet fantomas .` to the repo, commits the diff and pushes to the PR branch | -> | `/run ilverify` | IL verifier | runs `pwsh tests/ILVerify/ilverify.ps1`, reports failures; no push on failure | -> | `/run xlf` | localization tool | runs `dotnet build src/Compiler /t:UpdateXlf`, regenerates `.xlf` resource files, commits and pushes | -> | `/run test-baseline` | baseline test updater | runs `dotnet test` with `TEST_UPDATE_BSL=1` for the given filter, commits new `.bsl` files and pushes | +## Group D — Infrastructure & Maintenance ---- - -## Group D — Push, Validation & Tooling +Workflows: `branch-merge`, `copilot-setup-steps`, `skill-validation`, `agentic-state-machine`, `aw-auto-update`, `msbuild-quality-review`. ```mermaid stateDiagram-v2 - direction LR - state "branch-merge" as BM { - direction LR - [*] --> BM_Merge : ⚙️ push to main - [*] --> BM_Merge : ⚙️ push to release/* - BM_Merge --> [*] : 🤖 uses: dotnet/arcade inter-branch-merge-base.yml@main - } - state "skill-validation" as SV { - direction LR - [*] --> SV_Checkout : 👤 pull_request (paths: .github/skills/**, .github/agents/**) - [*] --> SV_Checkout : ⚙️ push (branches: main, same paths) - [*] --> SV_Checkout : 👤 workflow_dispatch (inputs: none) - SV_Checkout --> SV_Download : ⚙️ actions/checkout@v4 (sparse-checkout: .github/skills, .github/agents) - SV_Download --> SV_RunCheck : ⚙️ curl download skill-validator binary (dotnet/skills nightly release) - SV_RunCheck --> [*] : ⚙️ skill-validator check --skills --agents, write GITHUB_STEP_SUMMARY - } - state "copilot-setup-steps" as CSS { - direction LR - [*] --> CSS_Checkout : 👤 workflow_dispatch (inputs: none) - CSS_Checkout --> CSS_SetupDotnet : ⚙️ actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 (v4.2.2) - CSS_SetupDotnet --> CSS_RestoreCompiler : ⚙️ actions/setup-dotnet@v4 (global-json-file: global.json) - CSS_RestoreCompiler --> CSS_RestoreTools : ⚙️ ./build.sh -c Release --verbosity quiet || true - CSS_RestoreTools --> [*] : ⚙️ dotnet tool restore - } + direction LR + + state "branch-merge" as BM { + [*] --> BM_Run : ⚙️ push (release/*, main) + BM_Run --> [*] + } + + state "copilot-setup-steps" as CSS { + [*] --> CSS_Setup : 👤 dispatch + CSS_Setup --> [*] + } + + state "skill-validation" as SV { + [*] --> SV_Validate : 👤 PR / ⚙️ push (main) / 👤 dispatch + SV_Validate --> [*] + } + + state "agentic-state-machine" as ASM { + [*] --> ASM_Generate : ⏰ weekly / 👤 dispatch + state ASM_Changed <> + ASM_Generate --> ASM_Changed : ⚙️ doc differs from source + ASM_Changed --> ASM_PR : 🤖 create-pull-request + ASM_Changed --> ASM_Noop : ⚙️ no changes + ASM_PR --> [*] + ASM_Noop --> [*] + } + + state "aw-auto-update" as AWU { + [*] --> AWU_Install : 👤 dispatch + AWU_Install --> AWU_Upgrade : ⚙️ gh aw upgrade + AWU_Upgrade --> AWU_Compile : ⚙️ gh aw compile + AWU_Compile --> AWU_Diff : ⚙️ capture diff + state AWU_HasChanges <> + AWU_Diff --> AWU_HasChanges : ⚙️ check CHANGED_FILES + AWU_HasChanges --> AWU_Dedup : ⚙️ changes detected + AWU_HasChanges --> AWU_Noop : ⚙️ no changes + state AWU_DedupResult <> + AWU_Dedup --> AWU_DedupResult : ⚙️ search open PR/issue + AWU_DedupResult --> AWU_Noop : ⚙️ existing PR/issue found + AWU_DedupResult --> AWU_Delegate : ⚙️ no duplicate + AWU_Delegate --> [*] + AWU_Noop --> [*] + } + + state "msbuild-quality-review" as MQR { + [*] --> MQR_Discover : ⏰ weekly / 👤 dispatch + MQR_Discover --> MQR_Review : ⚙️ scan MSBuild files + MQR_Review --> MQR_Classify : ⚙️ apply rules A-E + state MQR_HasFindings <> + MQR_Classify --> MQR_HasFindings : ⚙️ check for issues + MQR_HasFindings --> MQR_Issue : 🤖 create-issue + MQR_HasFindings --> MQR_Noop : ⚙️ no new findings + MQR_Issue --> MQR_Fix : 🤖 create-pull-request (draft, safe fixes) + MQR_Fix --> [*] + MQR_Noop --> [*] + } ``` ---- - -## Labels +## Safe-Output Signatures -All labels in one place — who adds, removes, or reads each. **Cross-workflow flows** (e.g., `AI-Issue-Regression-PR` from `repo-assist` → consumed by `regression-pr-shepherd`) are visible here. +gh-aw safe-output defaults (suppressed below): `target: "*"`, `noop.report-as-issue: false`, `draft: false`. -**Workflow aliases used in this table** (matching the mermaid composite-state names): `ASM` = `agentic-state-machine`, `AWU` = `aw-auto-update`, `LFF` = `labelops-flake-fix`, `LPM` = `labelops-pr-maintenance`, `LPSS` = `labelops-pr-security-scan`, `RPS` = `regression-pr-shepherd`, `RA` = `repo-assist`, `ATP` = `add_to_project`. +| Workflow | Output | Max | Key Constraints | +|----------|--------|-----|-----------------| +| `agentic-state-machine` | `create-pull-request` | 1 | title `[Agentic State Machine] `, allowed-files `.github/docs/**` | +| `aw-auto-update` | `create-agent-session` | 1 | base: main | +| `labelops-flake-fix` | `create-pull-request` | 1 | title `[LabelOps Flake] `, labels: automation+Flaky+NO_RELEASE_NOTES, protected-files: fallback-to-issue | +| `labelops-flake-fix` | `create-issue` | 1 | title `[LabelOps Flake] `, labels: Flaky+automation | +| `labelops-flake-fix` | `add-comment` | 1 | — | +| `labelops-pr-maintenance` | `push-to-pull-request-branch` | 5 | protected-files: allowed | +| `labelops-pr-maintenance` | `add-comment` | 5 | hide-older-comments: true | +| `labelops-pr-maintenance` | `add-labels` | 3 | allowed: AI-needs-CI-fix-input | +| `labelops-pr-maintenance` | `dispatch-workflow` | 3 | workflows: labelops-flake-fix | +| `labelops-pr-security-scan` | `add-labels` | 50 | allowed: 10 labels (⚠️ Affects-* family + Scanned-Clean + Bypassed) | +| `labelops-pr-security-scan` | `add-comment` | 25 | hide-older-comments: true | +| `msbuild-quality-review` | `create-issue` | 1 | title `[msbuild-quality] `, labels: automation+Area-ProjectsAndBuild | +| `msbuild-quality-review` | `create-pull-request` | 1 | draft: true, title `[msbuild-quality] `, protected-files: fallback-to-issue | +| `regression-pr-shepherd` | `push-to-pull-request-branch` | 10 | allowed-files: tests/**, vsintegration/tests/** | +| `regression-pr-shepherd` | `add-comment` | 5 | hide-older-comments: true | +| `regression-pr-shepherd` | `remove-labels` | 5 | allowed: AI-thinks-issue-fixed | +| `repo-assist` | `create-pull-request` | 10 | title `Add regression test: `, labels: NO_RELEASE_NOTES+AI-Issue-Regression-PR, reviewers: abonie+T-Gro, auto-merge: true | +| `repo-assist` | `add-comment` | 10 | hide-older-comments: true | +| `repo-assist` | `add-labels` | 30 | allowed: AI-thinks-issue-fixed, AI-thinks-windows-only | +| `repo-assist` | `remove-labels` | 10 | allowed: AI-thinks-issue-fixed, AI-thinks-windows-only | +| `repo-assist` | `create-issue` | 4 | title `[Repo Assist] `, labels: automation+repo-assist | +| `repo-assist` | `push-to-pull-request-branch` | 4 | title `[Repo Assist] `, protected-files: fallback-to-issue | + +## Label Index | Label | Type | Added by | Removed by | Read by | Notes | -|---|---|---|---|---|---| -| `automation` | always-applied | LFF (PR), RA (issue) | — | — | via safe-output `labels:` (write-only — read by nothing) | -| `NO_RELEASE_NOTES` | always-applied | LFF, RA (PR) | — | check_release_notes | via safe-output `labels:` | -| `Flaky` | always-applied | LFF (PR + issue) | — | — | via safe-output `labels:` | -| `AI-Issue-Regression-PR` | always-applied | RA (PR), RPS (push-to-PR-branch) | — | RPS (selects PRs) | **cross-workflow signal RA → RPS** | -| `repo-assist` | always-applied | RA (issue) | — | — | via safe-output `labels:` | -| `AI-needs-CI-fix-input` | agent-add | LPM | — | — | escalation flag | -| `AI-Tooling-Check-Scanned-Clean` | agent-add | LPSS | — | — | per-PR scan outcome | -| `AI-Tooling-Check-Bypassed` | agent-add | LPSS | — | — | per-PR scan outcome | -| `⚠️ Affects-*` family (7) | agent-add | LPSS | — | — | Build-Infra, Compiler-Output, Bootstrap, Restore, Design-Time, Test-Tooling, Agent-Config | -| `⚠️ Suspicious-Prompting`, `⚠️ Scope-Review-Needed` | agent-add | LPSS | — | — | review-trigger flags | -| `AI-thinks-issue-fixed` | agent-add + agent-remove | RA | RPS, RA | — | **bidirectional** — RA proposes, RPS/RA retract | -| `AI-thinks-windows-only` | agent-add + agent-remove | RA | RA | — | RA self-corrects in Task 3 | -| `AI-Auto-Resolve-CI`, `AI-Auto-Resolve-Conflicts` | filter (read-only) | humans (manual, opt-in) | — | LPM (`gh pr list --search label:...`) | **selection signal into LPM** | -| `Needs-Triage` | imperative | ATP (`apply-label` job, github-script) | — | — | classic workflow, not gh-aw | +|-------|------|----------|------------|---------|-------| +| `Needs-Triage` | imperative | add_to_project | Human | Human | Applied to new issues | +| `NO_RELEASE_NOTES` | filter | Human | — | check_release_notes | Opts out of release notes check | +| `AI-Auto-Resolve-CI` | filter | Human | — | LPM | Opts PR into CI auto-fix | +| `AI-Auto-Resolve-Conflicts` | filter | Human | — | LPM | Opts PR into conflict auto-resolve | +| `AI-needs-CI-fix-input` | agent-add | LPM | Human | LPM (ci_blocked) | Escalation when CI unfixable | +| `AI-Issue-Regression-PR` | always-applied | RA (on PR) | — | RPS | Links regression test PRs | +| `AI-thinks-issue-fixed` | agent-add + agent-remove | RA | RA, RPS | RA (Task 2) | Issue believed fixed | +| `AI-thinks-windows-only` | agent-add + agent-remove | RA | RA | RA (Task 3) | Issue believed VS-only | +| `Flaky` | always-applied | LFF | — | Human | Marks flaky test PR/issue | +| `automation` | always-applied | LFF, MQR, RA | — | Human | Generic automation tag | +| `AI-Tooling-Check-Scanned-Clean` | agent-add | LPSS | — | Human | Fork PR scanned, no flags | +| `AI-Tooling-Check-Bypassed` | agent-add | LPSS | — | Human | Non-fork PR bypassed | +| `⚠️ Affects-*` family (7 labels) | agent-add | LPSS | — | Human | Build-Infra, Compiler-Output, Bootstrap, Restore, Design-Time, Test-Tooling, Agent-Config | +| `⚠️ Suspicious-Prompting` | agent-add | LPSS | — | Human | Prompt injection detected | +| `⚠️ Scope-Review-Needed` | agent-add | LPSS | — | Human | PR scope exceeds stated purpose | +| `repo-assist` | always-applied | RA | — | Human | Tags RA-created issues | +| `Area-ProjectsAndBuild` | always-applied | MQR | — | Human | Tags MSBuild quality issues | --- - +> generator-version: f107bba1a1cd61dc · source-shas: 06e56c52,149f0bbe,1af951a0,36b2b857,3775b51d,49b2989b,5e54b0e6,5e9a1344,7dca5b8f,9285c8a0,98d92f32,a5296399,acf12bdf,b5c04ea8,ec5fa486,f107bba1,