Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/scripts/__tests__/sync_pr_merge_contract.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ test('maint71 run writes reports and records a no-PR result with fake action cli
test('normalizeSyncHash accepts raw hashes and branch names', () => {
assert.equal(normalizeSyncHash('5108b94a2435'), '5108b94a2435');
assert.equal(normalizeSyncHash('sync/workflows-5108b94a2435'), '5108b94a2435');
assert.equal(normalizeSyncHash('sync/workflows-candidate'), 'candidate');
assert.equal(syncBranchForHash('5108b94a2435'), 'sync/workflows-5108b94a2435');
assert.equal(syncBranchForHash('candidate'), 'sync/workflows-candidate');
});

test('parseBooleanInput preserves explicit false values', () => {
Expand Down Expand Up @@ -223,6 +225,20 @@ test('selectActiveSyncPr honors target hash instead of newest PR', () => {
assert.deepEqual(selection.stale.map((item) => item.number), [2]);
});

test('selectActiveSyncPr can target the stable canary candidate branch', () => {
const selection = selectActiveSyncPr(
[
pr(1, 'sync/workflows-candidate', '2026-04-25T01:00:00Z'),
pr(2, 'sync/workflows-old-wave', '2026-04-25T02:00:00Z'),
],
'candidate',
);

assert.equal(selection.active.number, 1);
assert.equal(selection.expectedBranch, 'sync/workflows-candidate');
assert.deepEqual(selection.stale.map((item) => item.number), [2]);
});

test('selectActiveSyncPr reports missing target without marking stale PRs', () => {
const selection = selectActiveSyncPr(
[pr(1, 'sync/workflows-other', '2026-04-25T01:00:00Z')],
Expand Down
50 changes: 40 additions & 10 deletions .github/workflows/maint-68-sync-consumer-repos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ on:
workflow_dispatch:
inputs:
repos:
description: 'Comma-separated repos to sync (empty for all registered)'
description: 'Optional repo subset (canary phase accepts configured canaries only)'
type: string
required: false
dry_run:
Expand Down Expand Up @@ -83,6 +83,7 @@ jobs:
template_hash: ${{ steps.manifest.outputs.template_hash }}
plan_id: ${{ steps.manifest.outputs.plan_id }}
phase: ${{ steps.repos.outputs.phase }}
sync_branch: ${{ steps.repos.outputs.sync_branch }}
steps:
- name: Checkout
uses: actions/checkout@v7
Expand Down Expand Up @@ -124,6 +125,7 @@ jobs:
env:
CANARY_EVIDENCE_JSON: ${{ inputs.canary_evidence_json || '' }}
REPOS_INPUT: ${{ inputs.repos }}
TEMPLATE_HASH: ${{ steps.manifest.outputs.template_hash }}
run: |
if [ -n "$REPOS_INPUT" ]; then
repos="$REPOS_INPUT"
Expand All @@ -149,7 +151,16 @@ jobs:
--output sync-phase-selection.json
echo "matrix=$(jq -c '.matrix' sync-phase-selection.json)" >> "$GITHUB_OUTPUT"
echo "phase=$phase" >> "$GITHUB_OUTPUT"
if [ "$phase" = "canary" ]; then
# Candidate corrections update the same bounded PR in each canary
# repository. Only a plan-bound promotion gets immutable hash branches.
sync_branch="sync/workflows-candidate"
else
sync_branch="sync/workflows-$TEMPLATE_HASH"
fi
echo "sync_branch=$sync_branch" >> "$GITHUB_OUTPUT"
echo "Selected phase: $phase"
echo "Sync branch: $sync_branch"
jq -r '.selected_repos[]' sync-phase-selection.json

- name: Upload plan and prospective-diff evidence
Expand Down Expand Up @@ -713,11 +724,13 @@ jobs:
id: open_pr
if: steps.sync.outputs.has_changes == 'true' && inputs.dry_run != true
uses: actions/github-script@v9
env:
SYNC_BRANCH: ${{ needs.prepare.outputs.sync_branch }}
with:
github-token: ${{ env.REPO_TOKEN }}
script: |
const { isConsumerOpenPr } = require('./workflows/.github/scripts/sync_tracker_state');
const branchName = 'sync/workflows-${{ needs.prepare.outputs.template_hash }}';
const branchName = process.env.SYNC_BRANCH;
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const exists = await isConsumerOpenPr({
github,
Expand All @@ -737,10 +750,11 @@ jobs:
GH_TOKEN: ${{ env.REPO_TOKEN }}
DELIVERY_GENERATION: ${{ needs.prepare.outputs.template_hash }}
DELIVERY_REPOSITORY: ${{ matrix.repo }}
SYNC_BRANCH: ${{ needs.prepare.outputs.sync_branch }}
run: |
cd consumer

branch_name="sync/workflows-${{ needs.prepare.outputs.template_hash }}"
branch_name="$SYNC_BRANCH"

# Configure git for push/fetch authentication using credential helper
# This avoids exposing token in git remote URL or command output.
Expand All @@ -752,8 +766,10 @@ jobs:
git config credential.helper "$credential_helper"

# Keep an existing generated PR current instead of treating it as a
# terminal delivery attempt: rebuild its branch and refresh its lease
# only when the existing attempt still has a current delivery record.
# terminal delivery attempt. Immutable promotion branches refresh only
# within one generation. The stable canary branch may rotate to a new
# candidate plan when its existing delivery record is valid and owned
# by this consumer repository.
existing_pr=$(gh pr list --head "$branch_name" --json number -q '.[0].number' || echo "")
existing_head=""
existing_base=""
Expand All @@ -776,6 +792,8 @@ jobs:
DELIVERY_GENERATION="$DELIVERY_GENERATION" \
PLAN_ID="$PLAN_ID" \
DELIVERY_REPOSITORY="$DELIVERY_REPOSITORY" \
SYNC_PHASE="$SYNC_PHASE" \
SYNC_BRANCH="$branch_name" \
node -e '
const fs = require("fs");
const { parseDeliveryRecord, mergeEligibility } = require("../workflows/.github/scripts/sync_pr_lease_contract");
Expand All @@ -785,6 +803,21 @@ jobs:
process.stdout.write("false missing_or_invalid");
process.exit(0);
}
const isStableCandidate =
process.env.SYNC_PHASE === "canary" &&
process.env.SYNC_BRANCH === "sync/workflows-candidate";
if (isStableCandidate) {
if (record.repository !== (process.env.DELIVERY_REPOSITORY || "")) {
process.stdout.write("false repository_mismatch");
process.exit(0);
}
if (record.terminal_disposition) {
process.stdout.write(`false terminal:${record.terminal_disposition}`);
process.exit(0);
}
process.stdout.write("true candidate_plan_rotation");
process.exit(0);
}
Comment on lines +806 to +820

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 \
  'gh pr list --head|isConsumerOpenPr|head\.repo|head_repo|full_name|candidate_plan_rotation' \
  .github/workflows/maint-68-sync-consumer-repos.yml .github/scripts

Repository: stranske/Workflows

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- workflow lookup and refresh path ---'
sed -n '710,855p' .github/workflows/maint-68-sync-consumer-repos.yml

printf '%s\n' '--- isConsumerOpenPr implementation ---'
sed -n '390,455p' .github/scripts/sync_tracker_state/index.js

printf '%s\n' '--- all workflow copies ---'
find .github/workflows templates/consumer-repo/.github/workflows -maxdepth 1 -type f \
  \( -name 'maint-68-sync-consumer-repos.yml' -o -name 'maint-68-sync-consumer-repos.yaml' \) -print

Repository: stranske/Workflows

Length of output: 9627


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- workflow setup and refresh execution ---'
rg -n -C 8 \
  'actions/checkout|cd consumer|gh pr list|gh pr view|gh pr edit|existing_refreshable|existing_pr|git fetch origin' \
  .github/workflows/maint-68-sync-consumer-repos.yml

printf '%s\n' '--- template workflow inventory ---'
find templates -type f 2>/dev/null | head -80 || true
rg -n 'maint-68-sync-consumer-repos|sync/workflows-candidate' templates .github/workflows 2>/dev/null || true

printf '%s\n' '--- representative acceptance-path verifier ---'
python3 - <<'PY'
import re

workflow = open('.github/workflows/maint-68-sync-consumer-repos.yml', encoding='utf-8').read()
helper = open('.github/scripts/sync_tracker_state/index.js', encoding='utf-8').read()

lookup = re.search(
    r'existing_pr=\$\(gh pr list --head "\$branch_name" --json number -q \'\.\[0\]\.number\'',
    workflow,
)
view_body_only = 'gh pr view "$existing_pr" --json body -q .body' in workflow
fetch_origin_branch = 'git fetch origin "$branch_name"' in workflow
helper_uses_head_repo = bool(re.search(r'head\?\.repo|head\.repo|headRepo|full_name', helper[helper.index('async function isConsumerOpenPr'):helper.index('function formatStuckWindowMarker')]))
helper_returns_boolean = 'return pulls.some' in helper[helper.index('async function isConsumerOpenPr'):helper.index('function formatStuckWindowMarker')]

def candidate_accepts(record, delivery_repo):
    return (
        record.get('repository') == delivery_repo
        and not record.get('terminal_disposition')
    )

cases = [
    ('consumer PR', {'repository': 'owner/consumer', 'terminal_disposition': ''}),
    ('fork PR with forged body', {'repository': 'owner/consumer', 'terminal_disposition': ''}),
]
print({
    'lookup_requests_only_number': bool(lookup),
    'body_query_omits_head_repo': view_body_only,
    'origin_fetches_branch_before_refresh': fetch_origin_branch,
    'isConsumerOpenPr_reads_head_repo': helper_uses_head_repo,
    'isConsumerOpenPr_returns_only_match_boolean': helper_returns_boolean,
    'candidate_body_acceptance': {
        name: candidate_accepts(record, 'owner/consumer')
        for name, record in cases
    },
})
PY

Repository: stranske/Workflows

Length of output: 263


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- workflow setup and refresh execution ---'
rg -n -C 8 'actions/checkout|cd consumer|gh pr list|gh pr view|gh pr edit|existing_refreshable|existing_pr|git fetch origin' \
  .github/workflows/maint-68-sync-consumer-repos.yml || true

printf '%s\n' '--- template workflow inventory ---'
if [ -d templates ]; then
  find templates -type f | head -80
else
  echo 'templates directory is absent'
fi
rg -n 'maint-68-sync-consumer-repos|sync/workflows-candidate' templates .github/workflows 2>/dev/null || true

printf '%s\n' '--- representative acceptance-path verifier ---'
python3 - <<'PY'
import re

workflow = open('.github/workflows/maint-68-sync-consumer-repos.yml', encoding='utf-8').read()
helper = open('.github/scripts/sync_tracker_state/index.js', encoding='utf-8').read()

helper_start = helper.index('async function isConsumerOpenPr')
helper_end = helper.index('function formatStuckWindowMarker')
helper_body = helper[helper_start:helper_end]

lookup = re.search(
    r'existing_pr=\$\(gh pr list --head "\$branch_name" --json number -q',
    workflow,
)
print({
    'lookup_requests_only_number': bool(lookup),
    'body_query_omits_head_repo': 'gh pr view "$existing_pr" --json body -q .body' in workflow,
    'origin_fetches_branch_before_refresh': 'git fetch origin "$branch_name"' in workflow,
    'isConsumerOpenPr_reads_head_repo': bool(re.search(r'head\?\.repo|head\.repo|headRepo|full_name', helper_body)),
    'isConsumerOpenPr_returns_only_match_boolean': 'return pulls.some' in helper_body,
})

def candidate_accepts(record, delivery_repo):
    return (
        record.get('repository') == delivery_repo
        and not record.get('terminal_disposition')
    )

print({
    'same_repo_record': candidate_accepts(
        {'repository': 'owner/consumer', 'terminal_disposition': ''},
        'owner/consumer',
    ),
    'fork_pr_with_forged_same_repo_record': candidate_accepts(
        {'repository': 'owner/consumer', 'terminal_disposition': ''},
        'owner/consumer',
    ),
})
PY

Repository: stranske/Workflows

Length of output: 5077


🌐 Web query:

GitHub CLI gh pr list --head branch name fork pull requests documentation

💡 Result:

To list pull requests filtered by a specific head branch name in the GitHub CLI, use the --head flag with the gh pr list command [1][2]. The command syntax is: gh pr list --head Key details regarding the --head flag: - It filters pull requests by the specified head branch name [1][3]. - It does not support the ": " syntax (e.g., owner:branch) [1][4]. - The flag is case-sensitive and requires the exact branch name [1]. For example, to list all open pull requests where the head branch is named "feature-typo", run: gh pr list --head "feature-typo" This command works for pull requests regardless of whether they originate from a fork or the main repository, provided the branch name matches the specified filter [1][5]. If you need to filter by a specific repository (such as a fork) in addition to the branch name, you can use the -R or --repo flag to specify the target repository in the format [HOST/]OWNER/REPO [5][3].

Citations:


Validate the selected PR’s head repository before candidate rotation.

gh pr list --head "$branch_name" also matches fork PRs, but the lookup requests only the PR number. Candidate eligibility then trusts the PR-controlled record.repository field. Require the selected PR’s head.repo.full_name to equal ${{ matrix.repo }} before accepting rotation. isConsumerOpenPr is insufficient because it returns only a branch-match boolean.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/maint-68-sync-consumer-repos.yml around lines 806 - 820,
Update the selected PR lookup and candidate-rotation validation to retrieve the
PR head repository full name, then require it to equal the matrix repository
value before accepting rotation. Use this trusted head repository value rather
than the PR-controlled record.repository field, while preserving the existing
terminal-disposition and candidate-plan checks in isStableCandidate.

Source: Path instructions

if (record.generation !== (process.env.DELIVERY_GENERATION || "")) {
process.stdout.write("false generation_mismatch");
process.exit(0);
Expand Down Expand Up @@ -983,6 +1016,7 @@ jobs:
TEMPLATE_HASH: ${{ needs.prepare.outputs.template_hash }}
PLAN_ID: ${{ needs.prepare.outputs.plan_id }}
SYNC_PHASE: ${{ needs.prepare.outputs.phase }}
SYNC_BRANCH: ${{ needs.prepare.outputs.sync_branch }}
DRY_RUN: ${{ inputs.dry_run || 'false' }}
FORCE: ${{ inputs.force || 'false' }}
SYNC_OUTCOME: ${{ steps.sync.outcome || 'skipped' }}
Expand Down Expand Up @@ -1030,11 +1064,7 @@ jobs:
"template_hash": os.environ.get("TEMPLATE_HASH", ""),
"plan_id": os.environ.get("PLAN_ID", ""),
"sync_phase": os.environ.get("SYNC_PHASE", ""),
"expected_branch": (
f"sync/workflows-{os.environ.get('TEMPLATE_HASH', '')}"
if os.environ.get("TEMPLATE_HASH", "")
else ""
),
"expected_branch": os.environ.get("SYNC_BRANCH", ""),
"dry_run": dry_run,
"force": os.environ.get("FORCE", "false") == "true",
"has_changes": has_changes,
Expand Down
2 changes: 1 addition & 1 deletion docs/WORKFLOW_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ _Inline Gate helper_
- **`maint-62-integration-consumer.yml`** — Nightly + release-triggered integration tests that reuse `reusable-10-ci-python.yml` across multiple matrices and file/resolve the `integration-test` issue via the load-balanced API client (no extra app mint).
- **`maint-65-sync-label-docs.yml`** — Syncs `docs/LABELS.md` into every registered consumer repo (plus the integration tests repo) when the source doc changes or on demand, using the shared registered-repo helper and PAT gating for cross-repo pushes.
- **`maint-66-monthly-audit.yml`** — First-of-month workflow that gathers workflow-run stats, runs the API wrapper guard, and files/updates the monthly audit issue; relies on the shared API client so no extra npm installs or App-token mints are needed.
- **`maint-68-sync-consumer-repos.yml`** — Manifest-driven consumer sync that validates template/scripts, hashes the template set, records a prospective per-repo matrix, and opens PAT-backed sync PRs. Normal no-filter runs start with the configured canaries; a later `promote` run requires same-plan, green, review-clear Maint 71 evidence before it targets non-canaries. The jobs rely solely on the shared API client and repo PATs (no extra App token mints).
- **`maint-68-sync-consumer-repos.yml`** — Manifest-driven consumer sync that validates template/scripts, hashes the template set, records a prospective per-repo matrix, and opens PAT-backed sync PRs. Normal runs are fail-closed to the configured canaries, and explicit repo filters may only narrow that canary set. Candidate corrections refresh stable `sync/workflows-candidate` PRs; a later `promote` run requires same-plan, green, review-clear Maint 71 evidence before immutable hash-named branches target non-canaries. The jobs rely solely on the shared API client and repo PATs (no extra App token mints).
- **`maint-69-sync-integration-repo.yml`** — Keeps Workflows-Integration-Tests aligned with `templates/integration-repo/`, regenerates `requirements.lock`, and pushes updates using PATs; no GitHub App token mint is required because the workflow stays inside the two repos.
- **`maint-69-sync-labels.yml`** — Propagates the canonical `.github/labels-core.yml` set to every registered consumer repo (or a provided subset), reusing the registered-repo helper + load-balanced API client without any additional App-token minting.
- **`maint-70-fix-integration-formatting.yml`** — Manual formatter for Workflows-Integration-Tests that resolves the repo default branch, applies `black`+`ruff` fixes, and pushes via PAT only when a token is available; runs read-only otherwise.
Expand Down
2 changes: 1 addition & 1 deletion docs/ci/WORKFLOWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ Scheduled health jobs keep the automation ecosystem aligned:
* [`health-76-codex-cli-freshness.yml`](../../.github/workflows/health-76-codex-cli-freshness.yml) emits a weekly machine-readable freshness contract for the verifier `@openai/codex` CLI pin and uploads the deliberate update path as an artifact (scheduled weekly, manual dispatch).
* [`health-78-backplane-contract.yml`](../../.github/workflows/health-78-backplane-contract.yml) Workflows-internal gate that runs on PRs touching the run-contract/v1 contract set (schemas, registry, validator, fixtures): asserts the three schemas load as valid draft 2020-12 JSON Schema, `config/backplane_participants.json` keeps the required shape, and the bundled valid/invalid fixtures behave (the validator self-smoke).
* [`health-83-dependency-sync-efficiency.yml`](../../.github/workflows/health-83-dependency-sync-efficiency.yml) publishes a weekly, fixture-backed advisory report for dependency-bot, consumer-sync, and dev-tool-sync maintenance. It reports bounded-history limitations and comments on the dedicated efficiency durable tracker (`#2897`) only when the material-evidence fingerprint changes.
* [`maint-68-sync-consumer-repos.yml`](../../.github/workflows/maint-68-sync-consumer-repos.yml) pushes workflow template updates to registered consumer repos (release, template push, manual dispatch).
* [`maint-68-sync-consumer-repos.yml`](../../.github/workflows/maint-68-sync-consumer-repos.yml) stages workflow-template updates in stable PRs for the configured canaries. Explicit repo filters cannot broaden the canary phase; non-canaries are written only by a plan-bound `promote` run carrying green, review-clear Maint 71 evidence.
* [`maint-69-sync-integration-repo.yml`](../../.github/workflows/maint-69-sync-integration-repo.yml) syncs integration-repo templates to Workflows-Integration-Tests repository (template push, manual dispatch with dry-run support).
* [`maint-69-sync-labels.yml`](../../.github/workflows/maint-69-sync-labels.yml) syncs core functional labels from labels-core.yml to consumer repos (push to labels-core.yml, manual dispatch with dry-run support).
* [`maint-70-fix-integration-formatting.yml`](../../.github/workflows/maint-70-fix-integration-formatting.yml) applies Black and Ruff formatting fixes to Integration-Tests repository files (manual dispatch for CI formatting failures).
Expand Down
2 changes: 1 addition & 1 deletion docs/ci/WORKFLOW_SYSTEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,7 +738,7 @@ Keep this table handy when you are triaging automation: it confirms which workfl
| **Backplane Contract Integrity** (`health-78-backplane-contract.yml`, maintenance bucket) | `pull_request`, `push` (contract set: schemas, registry, validator, fixtures) | Workflows-internal gate over the run-contract/v1 contract set: asserts the three schemas load as valid draft 2020-12 JSON Schema, `config/backplane_participants.json` keeps the required shape, and the bundled valid/invalid fixtures behave (validator self-smoke). | ⚪ Required on contract PRs | [Backplane contract integrity runs](https://github.com/stranske/Workflows/actions/workflows/health-78-backplane-contract.yml) |
| **Health 83 Dependency Sync Efficiency** (`health-83-dependency-sync-efficiency.yml`, maintenance bucket) | `schedule` (weekly), `workflow_dispatch` | Publishes advisory lane, amplification, stale/replacement, and agent-exception evidence for dependency and generated sync work. Bounded collection is explicitly marked incomplete; the durable tracker changes only on a material-evidence fingerprint change. | ⚪ Scheduled/manual | [Dependency sync efficiency runs](https://github.com/stranske/Workflows/actions/workflows/health-83-dependency-sync-efficiency.yml) |
| **Reusable Backplane Conformance** (`reusable-backplane-conformance.yml`, reusable bucket) | `workflow_call` | Validate a participating repo's emitted run-contract/v1 envelope (producer/bridge) or ingested satellite object (consumer) against the canonical Workflows-owned schemas plus the opt-in participant registry. No-op for non-participants. | ⚪ Reusable (opt-in) | [Backplane conformance runs](https://github.com/stranske/Workflows/actions/workflows/reusable-backplane-conformance.yml) |
| **Maint 68 Sync Consumer Repos** (`maint-68-sync-consumer-repos.yml`, maintenance bucket) | `release`, `push` (templates), `workflow_dispatch` | Push workflow template updates to registered consumer repositories. Creates PRs in consumer repos when templates change. | ⚪ Automatic/manual | [Consumer sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-68-sync-consumer-repos.yml) |
| **Maint 68 Sync Consumer Repos** (`maint-68-sync-consumer-repos.yml`, maintenance bucket) | `release`, `schedule`, `workflow_dispatch` | Stage copied-file changes in stable configured-canary PRs, then write immutable hash-named PRs to non-canaries only after exact-plan Maint 71 promotion evidence passes. Explicit repo filters cannot bypass the canary boundary. | ⚪ Automatic/manual | [Consumer sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-68-sync-consumer-repos.yml) |
| **Maint 69 Sync Integration Repo** (`maint-69-sync-integration-repo.yml`, maintenance bucket) | `push` (templates), `workflow_dispatch` | Sync integration-repo templates to Workflows-Integration-Tests repository. Resolves drift detected by Health 67. Supports dry-run mode. | ⚪ Automatic/manual | [Integration sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-69-sync-integration-repo.yml) |
| **Maint 69 Sync Labels** (`maint-69-sync-labels.yml`, maintenance bucket) | `push` (labels-core.yml), `workflow_dispatch` | Sync core functional labels from labels-core.yml to consumer repositories. Distinguishes functional workflow labels from informational repo-specific labels. Supports dry-run mode. | ⚪ Automatic/manual | [Label sync runs](https://github.com/stranske/Workflows/actions/workflows/maint-69-sync-labels.yml) |
| **Fix Integration Tests Formatting** (`maint-70-fix-integration-formatting.yml`, maintenance bucket) | `workflow_dispatch` | Manually triggered workflow to apply Black and Ruff formatting fixes to Python files in the Workflows-Integration-Tests repository when CI formatting checks fail. | ⚪ Manual only | [Formatting fix runs](https://github.com/stranske/Workflows/actions/workflows/maint-70-fix-integration-formatting.yml) |
Expand Down
Loading
Loading